From ac56e7a5b93674c86cbb6e0a3d704ee4c2611fc0 Mon Sep 17 00:00:00 2001 From: Jan Vidar Krey Date: Thu, 19 Feb 2009 17:14:09 +0100 Subject: [PATCH] First git commit. --- AUTHORS | 6 + BUGS | 38 ++ COPYING | 674 ++++++++++++++++++ ChangeLog | 120 ++++ GNUmakefile | 245 +++++++ README | 66 ++ autotest/test_eventqueue.tcc | 89 +++ autotest/test_hub.tcc | 71 ++ autotest/test_inf.tcc | 155 +++++ autotest/test_ipfilter.tcc | 599 ++++++++++++++++ autotest/test_list.tcc | 137 ++++ autotest/test_memory.tcc | 37 + autotest/test_message.tcc | 521 ++++++++++++++ autotest/test_misc.tcc | 103 +++ autotest/test_tiger.tcc | 69 ++ doc/Doxyfile | 256 +++++++ doc/architecture.txt | 91 +++ doc/extensions.txt | 76 +++ doc/uhub.conf | 66 ++ doc/uhub.dot | 59 ++ doc/users.conf | 51 ++ release_setup.mk | 29 + release_targets.mk | 80 +++ src/adcconst.h | 142 ++++ src/adcrush.c | 950 ++++++++++++++++++++++++++ src/auth.c | 555 +++++++++++++++ src/auth.h | 91 +++ src/commands.c | 127 ++++ src/commands.h | 35 + src/config.c | 519 ++++++++++++++ src/config.h | 122 ++++ src/eventid.h | 37 + src/eventqueue.c | 172 +++++ src/eventqueue.h | 55 ++ src/hub.c | 950 ++++++++++++++++++++++++++ src/hub.h | 326 +++++++++ src/hubevent.c | 107 +++ src/hubevent.h | 45 ++ src/inf.c | 859 +++++++++++++++++++++++ src/inf.h | 54 ++ src/ipcalc.c | 425 ++++++++++++ src/ipcalc.h | 84 +++ src/list.c | 192 ++++++ src/list.h | 61 ++ src/log.c | 231 +++++++ src/log.h | 59 ++ src/main.c | 441 ++++++++++++ src/memory.c | 248 +++++++ src/memory.h | 45 ++ src/message.c | 836 +++++++++++++++++++++++ src/message.h | 206 ++++++ src/misc.c | 299 ++++++++ src/misc.h | 58 ++ src/netevent.c | 292 ++++++++ src/netevent.h | 53 ++ src/network-epoll.c | 326 +++++++++ src/network-kqueue.c | 290 ++++++++ src/network.c | 656 ++++++++++++++++++ src/network.h | 256 +++++++ src/plugin.h | 52 ++ src/route.c | 237 +++++++ src/route.h | 51 ++ src/sid.c | 61 ++ src/sid.h | 67 ++ src/tiger.c | 1241 ++++++++++++++++++++++++++++++++++ src/tiger.h | 26 + src/uhub.h | 168 +++++ src/user.c | 308 +++++++++ src/user.h | 261 +++++++ src/usermanager.c | 222 ++++++ src/usermanager.h | 101 +++ version.h | 15 + 72 files changed, 16652 insertions(+) create mode 100644 AUTHORS create mode 100644 BUGS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 GNUmakefile create mode 100644 README create mode 100644 autotest/test_eventqueue.tcc create mode 100644 autotest/test_hub.tcc create mode 100644 autotest/test_inf.tcc create mode 100644 autotest/test_ipfilter.tcc create mode 100644 autotest/test_list.tcc create mode 100644 autotest/test_memory.tcc create mode 100644 autotest/test_message.tcc create mode 100644 autotest/test_misc.tcc create mode 100644 autotest/test_tiger.tcc create mode 100644 doc/Doxyfile create mode 100644 doc/architecture.txt create mode 100644 doc/extensions.txt create mode 100644 doc/uhub.conf create mode 100644 doc/uhub.dot create mode 100644 doc/users.conf create mode 100644 release_setup.mk create mode 100644 release_targets.mk create mode 100644 src/adcconst.h create mode 100644 src/adcrush.c create mode 100644 src/auth.c create mode 100644 src/auth.h create mode 100644 src/commands.c create mode 100644 src/commands.h create mode 100644 src/config.c create mode 100644 src/config.h create mode 100644 src/eventid.h create mode 100644 src/eventqueue.c create mode 100644 src/eventqueue.h create mode 100644 src/hub.c create mode 100644 src/hub.h create mode 100644 src/hubevent.c create mode 100644 src/hubevent.h create mode 100644 src/inf.c create mode 100644 src/inf.h create mode 100644 src/ipcalc.c create mode 100644 src/ipcalc.h create mode 100644 src/list.c create mode 100644 src/list.h create mode 100644 src/log.c create mode 100644 src/log.h create mode 100644 src/main.c create mode 100644 src/memory.c create mode 100644 src/memory.h create mode 100644 src/message.c create mode 100644 src/message.h create mode 100644 src/misc.c create mode 100644 src/misc.h create mode 100644 src/netevent.c create mode 100644 src/netevent.h create mode 100644 src/network-epoll.c create mode 100644 src/network-kqueue.c create mode 100644 src/network.c create mode 100644 src/network.h create mode 100644 src/plugin.h create mode 100644 src/route.c create mode 100644 src/route.h create mode 100644 src/sid.c create mode 100644 src/sid.h create mode 100644 src/tiger.c create mode 100644 src/tiger.h create mode 100644 src/uhub.h create mode 100644 src/user.c create mode 100644 src/user.h create mode 100644 src/usermanager.c create mode 100644 src/usermanager.h create mode 100644 version.h diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..d14b8ae --- /dev/null +++ b/AUTHORS @@ -0,0 +1,6 @@ +Authors of uHub +=============== + +Jan Vidar Krey, Design and implementation + + diff --git a/BUGS b/BUGS new file mode 100644 index 0000000..e7880f7 --- /dev/null +++ b/BUGS @@ -0,0 +1,38 @@ +GENERIC: + +- BUG: SID allocator needs work for supporting the overflow case (1 million connections). + +- BUG: A passive client with no 'I4' (or 'I6') flag set, + cannot set it at any later point. + I4,I6,U4,U6 cannot be updated! + +- BUG? + No IQUI to disconnected user if they are disconnected by the hub. + Only peers receive that, but the disconnected user will receive a STA + (status) message. + The ADC spec is unclear on this point. + +- Some very rare abort or crashes. + Window hub just stop without actually crashing. + Seem to be some weird memory race conditions also. + +- Exceeding send queue by just joining. Need to use dynamic send queue + based on number of clients. + +- A memory leak in hub_handle_info() -> update_user_info() -> adc_msg_copy(). + +- Get rid of list.c, use TAILQ instead! + +Feature enhancements: +- No support for changing nick names. + +Todo: +* Commands + * !kick_nick + * !kick_cid + * !kick_ip + * !reload + * !stats + * +myip + * !help + diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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. + + + Copyright (C) + + 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 . + +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: + + Copyright (C) + 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 +. + + 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 +. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..dd24fdd --- /dev/null +++ b/ChangeLog @@ -0,0 +1,120 @@ +0.2.5-3487: +- Fixed out of memory situations, used when uhub operates on a limited heap. +- Code cleanups, reduced heap memory footprint. +- Fixed bug throwing users out due to exessive send queue when logging into a large hub. +- Added some simple hub commands (!stats, !uptime, !version, !help, etc). + + +0.2.4-3470: +- Added option chat_is_privileged, which makes chat for privileged users only. +- Started working on hub commands. +- Fixed a double free() related crash / abort. +- Fixed several send() -> EPIPE related crashes. + + +0.2.3-3429: +- Fixed one crash bug (jump to NULL). +- Fixed IPv6 dual stack issues on Winsock, but needs Vista or later to compile. +- Disable IPv6 if dual stack is not supported (WinXP with IPv6). +- Fixed bind issue for IPv4 addresses. +- Made sure no chat message could have PM context flag, unless set by the hub. +- Ignore empty INF update messages. + + +0.2.2-3393: +- Fixed a crash related to hub login. +- Added some fixes for older versions of libevent +- Added option to log messages through syslog. +- Added low bandwidth mode for really big hubs. +- Started writing a benchmark tool for the hub. +- Fix bug: reload configuration reset uptime. +- Experimental support for NetBSD 3.1. + + +0.2.1-3321: +- Added more robust configuration file parsing. +- Use defaults if config file is not found, unless a file is specified. +- Added user class super user (above operator, below admin). +- Added NAT override for clients behind the same NAT as the hub. +- Fixed a bug summarizing shared size and files for the PING extension. +- Fixed bugs related to hub limits. + + +0.2.0-3293: +- Fixed multiple crash bugs. +- Fully compatible with ADC/1.0. +- Protocol extensions: 'TIGR' and 'PING' +- Added support for configuring min/max share size, slots, concurrent hubs, not very well tested. +- Made all status message strings configurable. +- Various BSD issues fixed. Should perform equally well as Linux. +- Allow ignored configuration options (used when deprecating configuration options). +- Added command line options for checking configuration. +- A windows port is more or less done (MinGW) +- Lots of new auto tests added. + + +0.1.6-2608: +- Changes required for the ADC/0.14 specification. +- Stability fixes. +- Win32 fixes, it compiles and runs, but not quite ported yet. +- Added CMAKE files, which can be used instead of GNU make. +- Made sure all messages are terminated when created. +- Use length of messages, instead of strlen() to determine them. +- Added more asserts for messages. Spotted a few errors as a result of that. +- Added support for more sophisticated memory allocation debugging. + + +0.1.5-2462 +- Fixed double free (crash). +- Fixed password challenge/response coding error (crash). +- Changes required for the new ADC/0.13 specification. +- Fixed IPv6 netmask matching (banning) +- Added UDP server, needed for auto-configure extension (AUT0) +- Send 'ping' messages periodically if nothing heard from clients. +- Print IP when client disconnects. +- Lots of automatic testcases added, fix many bugs in the process. +- GCC 2.95 compile fixes. + + +0.1.4-2301: +- uHub now requires and utilizes libevent. This allows for greater code +portability, and less code complexity. +- Various FreeBSD/OpenBSD/NetBSD fixes have been applied. +- Can now log files other than stderr. +- Added several automatic testcases. +- The application should now be much more stable, and never consume much CPU. +- Fixed several small annoying bugs. + + +0.1.3-2147: +- Changed license to GPL3 +- Fixed several crashes +- Major code cleanups +- Refactored event handling +- Log file format change (minor) +- Automatic regression testing of code base (via exotic). +- Memory handling debug infrastructure. + + +0.1.2-2020: +- Fix infinite loops +- Don't log users leaving unless they are logged in. +- Fix private messages in chat only hubs. +- Operators/admins override chat only hub settings. +- Fix client/server protocol support negotiation handling +- IP banning should now work (IPv6 is not tested yet). + + +0.1.1-1956: +- Fixed memory leaks in ACL handling +- Prevent unneeded malloc's in command handling when buffers are big enough. +- Code cleanups and more doxygen style comments added. +- Fixed crashes and infinite loops +- FreeBSD compile fixes +- Timestamp log messages. +- Log network/bandwidth statistics + + +0.1.0-1840: +- First public release + diff --git a/GNUmakefile b/GNUmakefile new file mode 100644 index 0000000..8f18f5d --- /dev/null +++ b/GNUmakefile @@ -0,0 +1,245 @@ + ## +## Makefile for uhub (Use GNU make) +## Copyright (C) 2007-2008, Jan Vidar Krey + # + +CC = gcc +LD := $(CC) +MV := mv +RANLIB := ranlib +CFLAGS += -pipe -Wall +USE_PCH ?= YES +USE_SSL ?= NO +USE_BIGENDIAN ?= AUTO +BITS ?= AUTO +SILENT ?= YES +LDLIBS += -levent + + +ifeq ($(OS), Windows_NT) +WINDOWS ?= YES +endif + +ifeq ($(WINDOWS),YES) +USE_BIGENDIAN := NO +LDLIBS += -lws2_32 +UHUB_CONF_DIR ?= c:/uhub/ +UHUB_PREFIX ?= c:/uhub/ +CFLAGS += -mno-cygwin +LDFLAGS += -mno-cygwin +BIN_EXT ?= .exe +else +UHUB_CONF_DIR ?= /etc/uhub +UHUB_PREFIX ?= /usr/local +CFLAGS += -I/usr/local/include +LDFLAGS += -L/usr/local/lib +BIN_EXT ?= +endif + +ifeq ($(SILENT),YES) + MSG_CC=@echo " CC:" $(notdir $^) && + MSG_PCH=@echo " PCH:" $(notdir $@) && + MSG_LD=@echo " LD:" $(notdir $@) && + MSG_AR=@echo " AR:" $(notdir $@) && +else + MSG_CC= + MSG_PCH= + MSG_LD= + MSG_AR= +endif + + +CFLAGS += -I/source/libevent +LDFLAGS += -L/source/libevent + +-include release_setup.mk +ifeq ($(RELEASE),YES) +CFLAGS += -Os -DNDEBUG -fstack-protector-all +else +CFLAGS += -g -DDEBUG -fstack-protector-all +endif + +ifeq ($(PROFILING),YES) +CFLAGS += -pg +LDFLAGS += -pg +endif + +ifeq ($(FUNCTRACE),YES) +CFLAGS += -finstrument-functions +CFLAGS += -DDEBUG_FUNCTION_TRACE +endif + +ifeq ($(USE_PCH),YES) +PCHSRC=src/uhub.h +PCH=src/uhub.h.gch +else +PCH= +endif + +ifneq ($(BITS), AUTO) +ifeq ($(BITS), 64) +CFLAGS += -m64 +LDFLAGS += -m64 +else +ifeq ($(BITS), 32) +CFLAGS += -m32 +LDFLAGS += -m32 +endif +endif +endif + +ifeq ($(USE_BIGENDIAN),AUTO) +ifeq ($(shell perl -e 'print pack("L", 0x554E4958)'),UNIX) +CFLAGS += -DARCH_BIGENDIAN +endif +else +ifeq ($(USE_BIGENDIAN),YES) +CFLAGS += -DARCH_BIGENDIAN +endif +endif + +ifeq ($(USE_SSL),YES) +CFLAGS += -DSSL_SUPPORT +LDLIBS += -lssl +endif + +ifneq ($(LIBEVENT_PATH),) +CFLAGS += -I$(LIBEVENT_PATH) +LDFLAGS += -L$(LIBEVENT_PATH) +endif + +# Sources +libuhub_SOURCES := \ + src/auth.c \ + src/commands.c \ + src/config.c \ + src/eventqueue.c \ + src/hubevent.c \ + src/hub.c \ + src/inf.c \ + src/ipcalc.c \ + src/list.c \ + src/log.c \ + src/memory.c \ + src/message.c \ + src/misc.c \ + src/netevent.c \ + src/network.c \ + src/route.c \ + src/sid.c \ + src/tiger.c \ + src/user.c \ + src/usermanager.c + +uhub_SOURCES := src/main.c + +adcrush_SOURCES := src/adcrush.c + +uhub_HEADERS := \ + src/adcconst.h \ + src/auth.h \ + src/config.h \ + src/eventid.h \ + src/eventqueue.h \ + src/hubevent.h \ + src/hub.h \ + src/inf.h \ + src/ipcalc.h \ + src/list.h \ + src/log.h \ + src/memory.h \ + src/message.h \ + src/misc.h \ + src/netevent.h \ + src/network.h \ + src/route.h \ + src/sid.h \ + src/tiger.h \ + src/uhub.h \ + src/user.h \ + src/usermanager.h + +autotest_SOURCES := \ + autotest/test_message.tcc \ + autotest/test_list.tcc \ + autotest/test_memory.tcc \ + autotest/test_ipfilter.tcc \ + autotest/test_inf.tcc \ + autotest/test_hub.tcc \ + autotest/test_misc.tcc \ + autotest/test_tiger.tcc \ + autotest/test_eventqueue.tcc + +autotest_OBJECTS = autotest.o + +# Source to objects +libuhub_OBJECTS := $(libuhub_SOURCES:.c=.o) +uhub_OBJECTS := $(uhub_SOURCES:.c=.o) +adcrush_OBJECTS := $(adcrush_SOURCES:.c=.o) + +all_OBJECTS := $(libuhub_OBJECTS) $(uhub_OBJECTS) $(adcrush_OBJECTS) $(autotest_OBJECTS) + +LIBUHUB=libuhub.a +uhub_BINARY=uhub$(BIN_EXT) +adcrush_BINARY=adcrush$(BIN_EXT) +autotest_BINARY=autotest/test$(BIN_EXT) + +%.o: %.c + $(MSG_CC) $(CC) -c $(CFLAGS) -o $@.tmp $^ && \ + $(MV) $@.tmp $@ + +all: $(uhub_BINARY) $(PCH) + +$(adcrush_BINARY): $(PCH) $(LIBUHUB) $(adcrush_OBJECTS) + $(MSG_LD) $(CC) -o $@.tmp $(adcrush_OBJECTS) $(LIBUHUB) $(LDFLAGS) $(LDLIBS) && \ + $(MV) $@.tmp $@ + +$(uhub_BINARY): $(PCH) $(LIBUHUB) $(uhub_OBJECTS) + $(MSG_LD) $(CC) -o $@.tmp $(uhub_OBJECTS) $(LIBUHUB) $(LDFLAGS) $(LDLIBS) && \ + $(MV) $@.tmp $@ + +$(LIBUHUB): $(libuhub_OBJECTS) + $(MSG_AR) $(AR) rc $@.tmp $^ && \ + $(RANLIB) $@.tmp && \ + $(MV) $@.tmp $@ + +ifeq ($(USE_PCH),YES) +$(PCH): $(uhub_HEADERS) + $(MSG_PCH) $(CC) $(CFLAGS) -o $@.tmp $(PCHSRC) && \ + $(MV) $@.tmp $@ +endif + +$(autotest_OBJECTS): autotest.c + $(MSG_CC) $(CC) -c $(CFLAGS) -Isrc -o $@.tmp $< && \ + $(MV) $@.tmp $@ + +$(autotest_BINARY): $(autotest_OBJECTS) $(LIBUHUB) + $(MSG_LD) $(CC) -o $@.tmp $^ $(LDFLAGS) $(LDLIBS) && \ + $(MV) $@.tmp $@ + +autotest: $(autotest_BINARY) + @./$(autotest_BINARY) -s -f + +ifeq ($(WINDOWS),YES) +install: + @echo "Cannot install automatically on windows." +else +install: $(uhub_BINARY) + @echo Copying $(uhub_BINARY) to $(UHUB_PREFIX)/bin/ + @cp $(uhub_BINARY) $(UHUB_PREFIX)/bin/ + @if [ ! -d $(UHUB_CONF_DIR) ]; then echo Creating $(UHUB_CONF_DIR); mkdir -p $(UHUB_CONF_DIR); fi + @if [ ! -f $(UHUB_CONF_DIR)/uhub.conf ]; then cp doc/uhub.conf $(UHUB_CONF_DIR); fi + @if [ ! -f $(UHUB_CONF_DIR)/users.conf ]; then cp doc/users.conf $(UHUB_CONF_DIR); fi + @touch $(UHUB_CONF_DIR)/motd + @echo done. +endif + +dist-clean: + @rm -rf $(all_OBJECTS) $(PCH) *~ core + +clean: + @rm -rf $(libuhub_OBJECTS) $(PCH) *~ core $(uhub_BINARY) $(LIBUHUB) $(all_OBJECTS) && \ + echo Clean as a whistle + +-include release_targets.mk + diff --git a/README b/README new file mode 100644 index 0000000..bc8913c --- /dev/null +++ b/README @@ -0,0 +1,66 @@ + +Welcome and thanks for downloading uHub. +See the COPYING file for licensing details (GPLv3). + + +SUPPORTED PLATFORMS AND PREREQUISITES + +Getting started should be fairly straight forward if you are +on Linux, BSD or MacOSX. + +In any case you will need GNU make for compiling uHub. +The makefiles depends on Perl for autodetect the endianess of the +host system, but this can be worked around by specifying +BIGENDIAN=YES or NO as a parameter for make (default is AUTO). + +uHub requires libevent to be installed, you can download libevent +from http://monkey.org/~provos/libevent/ if you do not already +have it. + +On Windows you will need MinGW in order to compile, and you might +have to use (make CC=gcc). + + +COMPILING + +- run "make" + +Make sure you have GNU make. On some systems, such as FreeBSD, +you have to run "gmake" instead of the traditional BSD make. + + +INSTALLING + +Either run "make install", or: + +- create the directory /etc/uhub +- copy ./doc/uhub.conf to /etc/uhub +- copy ./doc/users.conf to /etc/uhub + +You should now be able to start the hub (using: ./uhub). + + +CONFIGURATION + +To see help, use ./uhub -h + +The default configuration file is fairly straight forward +and will explain roughly what all the different configuration +directive does. + +When you have reconfigured the hub, you don't have to restart +it, but rather send a 'HUP' signal to the server, and it will +reload the configuration files, user database and message +of the day (motd). + +Use: "kill -HUP {pid of uhub}", or "killall -HUP uhub". + + +REPORTING PROBLEMS + +Send bugs reports, questions, feature requests, etc. to our +e-mail address: < uhub (at) extatic (dot) org > + +However, see the file 'BUGS' for a list of known bugs. + + diff --git a/autotest/test_eventqueue.tcc b/autotest/test_eventqueue.tcc new file mode 100644 index 0000000..362e8cc --- /dev/null +++ b/autotest/test_eventqueue.tcc @@ -0,0 +1,89 @@ +#include + +static struct event_queue* eq; +static int eq_val; +struct event* libevent_handle; + +static void eq_callback(void* callback_data, struct event_data* event_data) +{ + eq_val += event_data->id; +} + +EXO_TEST(eventqueue_init_1, { + eq = 0; + eq_val = 0; + return event_queue_initialize(&eq, eq_callback, &eq_val) == 0 && event_queue_size(eq) == 0; +}); + +EXO_TEST(eventqueue_init_2, { + /* hack */ + libevent_handle = eq->event; + eq->event = 0; + return eq->callback_data == &eq_val && eq->callback == eq_callback && eq->q1 && eq->q2 && !eq->locked; +}); + +EXO_TEST(eventqueue_post_1, { + struct event_data message; + message.id = 0x1001; + message.ptr = &message; + message.flags = message.id * 2; + event_queue_post(eq, &message); + return event_queue_size(eq) == 1; +}); + +EXO_TEST(eventqueue_process_1, { + event_queue_process(eq); + return eq_val == 0x1001; +}); + +EXO_TEST(eventqueue_size_1, { + eq_val = 0; + return event_queue_size(eq) == 0; +}); + +EXO_TEST(eventqueue_post_2, { + struct event_data message; + message.id = 0x1002; + message.ptr = &message; + message.flags = message.id * 2; + event_queue_post(eq, &message); + return event_queue_size(eq) == 1; +}); + +EXO_TEST(eventqueue_size_2, { + eq_val = 0; + return event_queue_size(eq) == 1; +}); + + +EXO_TEST(eventqueue_post_3, { + struct event_data message; + message.id = 0x1003; + message.ptr = &message; + message.flags = message.id * 2; + event_queue_post(eq, &message); + return event_queue_size(eq) == 2; +}); + +EXO_TEST(eventqueue_size_3, { + eq_val = 0; + return event_queue_size(eq) == 2; +}); + +EXO_TEST(eventqueue_process_2, { + event_queue_process(eq); + return eq_val == 0x2005; +}); + +EXO_TEST(eventqueue_size_4, { + eq_val = 0; + return event_queue_size(eq) == 0; +}); + +EXO_TEST(eventqueue_shutdown_1, { + eq->event = libevent_handle; + event_queue_shutdown(eq); + return 1; +}); + + diff --git a/autotest/test_hub.tcc b/autotest/test_hub.tcc new file mode 100644 index 0000000..d29fe5c --- /dev/null +++ b/autotest/test_hub.tcc @@ -0,0 +1,71 @@ +#include + +static struct hub_config g_config; +static struct acl_handle g_acl; +static struct hub_info* g_hub; + +/* +static void create_test_user() +{ + if (g_user) + return; + + g_user = (struct user*) malloc(sizeof(struct user)); + memset(g_user, 0, sizeof(struct user)); + memcpy(g_user->id.nick, "exotic-tester", 13); + g_user->sid = 1; +} +*/ + +EXO_TEST(hub_net_startup, { + return (net_initialize() != -1); +}); + +EXO_TEST(hub_config_initialize, { + config_defaults(&g_config); + return 1; +}); + +EXO_TEST(hub_acl_initialize, { + return (acl_initialize(&g_config, &g_acl) != -1); +}); + +EXO_TEST(hub_service_initialize, { + g_hub = hub_start_service(&g_config); + return g_hub ? 1 : 0; +}); + +EXO_TEST(hub_variables_startup, { + hub_set_variables(g_hub, &g_acl); + return 1; +}); + +/*** HUB IS OPERATIONAL HERE! ***/ + +EXO_TEST(hub_variables_shutdown, { + hub_free_variables(g_hub); + return 1; +}); + +EXO_TEST(hub_acl_shutdown, { + acl_shutdown(&g_acl); + return 1; +}); + +EXO_TEST(hub_config_shutdown, { + free_config(&g_config); + return 1; +}); + +EXO_TEST(hub_service_shutdown, { + if (g_hub) + { + hub_shutdown_service(g_hub); + return 1; + } + return 0; +}); + +EXO_TEST(hub_net_shutdown, { + return (net_shutdown() != -1); +}); diff --git a/autotest/test_inf.tcc b/autotest/test_inf.tcc new file mode 100644 index 0000000..f693dca --- /dev/null +++ b/autotest/test_inf.tcc @@ -0,0 +1,155 @@ +#include + +#define USER_CID "GNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI" +#define USER_PID "3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y" +#define USER_NICK "Friend" +#define USER_SID "AAAB" + +static struct user* inf_user = 0; +static struct hub_info* inf_hub = 0; + +extern int hub_handle_info_login(struct user* user, struct adc_message* cmd); + +static void inf_create_hub() +{ + inf_hub = (struct hub_info*) hub_malloc_zero(sizeof(struct hub_info)); + inf_hub->users = (struct user_manager*) hub_malloc_zero(sizeof(struct user_manager)); + inf_hub->users->list = list_create(); + inf_hub->users->free_sid = 1; + + inf_hub->acl = (struct acl_handle*) hub_malloc_zero(sizeof(struct acl_handle)); + inf_hub->config = (struct hub_config*) hub_malloc_zero(sizeof(struct hub_config)); + + config_defaults(inf_hub->config); + acl_initialize(inf_hub->config, inf_hub->acl); +} + +static void inf_destroy_hub() +{ + /* FIXME */ +} + + +static void inf_create_user() +{ + if (inf_user) return; + inf_user = (struct user*) hub_malloc_zero(sizeof(struct user)); + inf_user->id.sid = 1; + inf_user->sd = -1; + inf_user->hub = inf_hub; + inf_user->limits.upload_slots = 1; +} + +static void inf_destroy_user() +{ + if (!inf_user) return; + hub_free(inf_user); + inf_user = 0; +} + +EXO_TEST(inf_create_setup, +{ + inf_create_hub(); + inf_create_user(); + return (inf_user && inf_hub); +}); + + + +#define CHECK_INF(MSG, EXPECT) \ + struct adc_message* msg = adc_msg_parse_verify(inf_user, MSG, strlen(MSG)); \ + int ok = hub_handle_info_login(inf_user, msg); \ + adc_msg_free(msg); \ + if (ok != EXPECT) \ + printf("Expected %d, got %d\n", EXPECT, ok); \ + return ok == EXPECT; + + +EXO_TEST(inf_ok_1, { CHECK_INF("BINF AAAB NIFriend IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y\n", 0); }); + +/* check CID abuse */ +EXO_TEST(inf_cid_1, { CHECK_INF("BINF AAAB NIFriend PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y\n", status_msg_inf_error_cid_missing); }); +EXO_TEST(inf_cid_2, { CHECK_INF("BINF AAAB NIFriend IDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y\n", status_msg_inf_error_cid_invalid); }); +EXO_TEST(inf_cid_3, { CHECK_INF("BINF AAAB NIFriend IDaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y\n", status_msg_inf_error_cid_invalid); }); +EXO_TEST(inf_cid_4, { CHECK_INF("BINF AAAB NIFriend IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2R PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y\n", status_msg_inf_error_cid_invalid); }); /* cid 1 byte short */ +EXO_TEST(inf_cid_5, { CHECK_INF("BINF AAAB NIFriend IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RX PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y\n", status_msg_inf_error_cid_invalid); }); /* cid 1 byte longer */ +EXO_TEST(inf_cid_6, { CHECK_INF("BINF AAAB NIFriend IDA PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y\n", status_msg_inf_error_cid_invalid); }); +EXO_TEST(inf_cid_7, { CHECK_INF("BINF AAAB NIFriend IDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y\n", status_msg_inf_error_cid_invalid); }); /* multi */ +EXO_TEST(inf_cid_8, { CHECK_INF("BINF AAAB NIFriend IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y IDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n", status_msg_inf_error_cid_invalid); }); +EXO_TEST(inf_cid_9, { CHECK_INF("BINF AAAB NIFriend IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI\n", status_msg_inf_error_cid_invalid); }); + +/* equivalent to the pid versions */ +EXO_TEST(inf_pid_1, { CHECK_INF("BINF AAAB NIFriend IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI\n", status_msg_inf_error_pid_missing); }); /* pid missing */ +EXO_TEST(inf_pid_2, { CHECK_INF("BINF AAAB NIFriend ID3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y PDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n", status_msg_inf_error_cid_invalid); }); /* variant of inf_cid_2 */ +EXO_TEST(inf_pid_3, { CHECK_INF("BINF AAAB NIFriend IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI PDaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", status_msg_inf_error_pid_invalid); }); /* pid invalid */ +EXO_TEST(inf_pid_4, { CHECK_INF("BINF AAAB NIFriend IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7\n", status_msg_inf_error_pid_invalid); }); /* pid 1 byte short */ +EXO_TEST(inf_pid_5, { CHECK_INF("BINF AAAB NIFriend IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7YX\n", status_msg_inf_error_pid_invalid); }); /* pid 1 byte longer */ +EXO_TEST(inf_pid_6, { CHECK_INF("BINF AAAB NIFriend IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI PDA\n", status_msg_inf_error_pid_invalid); }); /* very short pid */ +EXO_TEST(inf_pid_7, { CHECK_INF("BINF AAAB NIFriend IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y PDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n", status_msg_inf_error_pid_invalid); }); +EXO_TEST(inf_pid_8, { CHECK_INF("BINF AAAB NIFriend PDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y\n", status_msg_inf_error_pid_invalid); }); +EXO_TEST(inf_pid_9, { CHECK_INF("BINF AAAB NIFriend IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y\n", status_msg_inf_error_pid_invalid); }); + +/* check nickname abuse */ +EXO_TEST(inf_nick_01, { CHECK_INF("BINF AAAB IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y\n", status_msg_inf_error_nick_missing); }); +EXO_TEST(inf_nick_02, { CHECK_INF("BINF AAAB NI IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y\n", status_msg_inf_error_nick_short); }); +EXO_TEST(inf_nick_03, { CHECK_INF("BINF AAAB NIa IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y\n", status_msg_inf_error_nick_short); }); +EXO_TEST(inf_nick_04, { CHECK_INF("BINF AAAB NIabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y\n", status_msg_inf_error_nick_long); }); +EXO_TEST(inf_nick_05, { CHECK_INF("BINF AAAB NI\\sabc IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y\n", status_msg_inf_error_nick_spaces); }); +EXO_TEST(inf_nick_06, { CHECK_INF("BINF AAAB NIa\\sc IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y\n", 0); }); +EXO_TEST(inf_nick_07, { CHECK_INF("BINF AAAB NIa\tc IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y\n", status_msg_inf_error_nick_bad_chars); }); +EXO_TEST(inf_nick_08, { CHECK_INF("BINF AAAB NIa\\nc IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y\n", status_msg_inf_error_nick_bad_chars); }); +EXO_TEST(inf_nick_09, { CHECK_INF("BINF AAAB NIabc NIdef IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y\n", status_msg_inf_error_nick_multiple); }); +EXO_TEST(inf_nick_10, { + const char* line = "BINF AAAB IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y\n"; + char nick[10]; + nick[0] = 0xf7; nick[1] = 0x80; nick[2] = 0x7f; nick[3] = 0x81; nick[4] = 0x98; nick[5] = 0x00; + struct adc_message* msg = adc_msg_parse_verify(inf_user, line, strlen(line)); + + adc_msg_add_named_argument(msg, "NI", nick); + int ok = hub_handle_info_login(inf_user, msg); + adc_msg_free(msg); + if (ok != status_msg_inf_error_nick_not_utf8) + printf("Expected %d, got %d\n", status_msg_inf_error_nick_not_utf8, ok); + return ok == status_msg_inf_error_nick_not_utf8; +}); + +/* check limits for slots and share */ +EXO_TEST(inf_limits_1, { inf_hub->config->limit_min_slots = 1; CHECK_INF("BINF AAAB NIFriend IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y SL0\n", status_msg_user_slots_low); }); +EXO_TEST(inf_limits_2, { inf_hub->config->limit_max_slots = 5; CHECK_INF("BINF AAAB NIFriend IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y SL99\n", status_msg_user_slots_high); }); +EXO_TEST(inf_limits_3, { inf_hub->config->limit_min_share = 100; CHECK_INF("BINF AAAB NIFriend IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y SS104857599\n", status_msg_user_share_size_low); }); +EXO_TEST(inf_limits_4, { inf_hub->config->limit_max_share = 500; CHECK_INF("BINF AAAB NIFriend IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y SS524288001\n", status_msg_user_share_size_high); }); + +/* setup for check limits for hubs */ +EXO_TEST(inf_limit_hubs_setup, +{ + inf_hub->config->limit_min_slots = 0; + inf_hub->config->limit_max_slots = 0; + inf_hub->config->limit_max_share = 0; + inf_hub->config->limit_min_share = 0; + inf_hub->config->limit_max_hubs_user = 10; + inf_hub->config->limit_max_hubs_reg = 10; + inf_hub->config->limit_max_hubs_op = 10; + inf_hub->config->limit_min_hubs_user = 2; + inf_hub->config->limit_min_hubs_reg = 2; + inf_hub->config->limit_min_hubs_op = 2; + inf_hub->config->limit_max_hubs = 25; + + return 1; +} ); + + +EXO_TEST(inf_limit_hubs_1, { CHECK_INF("BINF AAAB NIFriend IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y HN15\n", status_msg_user_hub_limit_high); }); +EXO_TEST(inf_limit_hubs_2, { CHECK_INF("BINF AAAB NIFriend IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y HN1\n", status_msg_user_hub_limit_low); }); +EXO_TEST(inf_limit_hubs_3, { CHECK_INF("BINF AAAB NIFriend IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y HO15\n", status_msg_user_hub_limit_high); }); +EXO_TEST(inf_limit_hubs_4, { CHECK_INF("BINF AAAB NIFriend IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y HO1\n", status_msg_user_hub_limit_low); }); +EXO_TEST(inf_limit_hubs_5, { CHECK_INF("BINF AAAB NIFriend IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y HR15\n", status_msg_user_hub_limit_high); }); +EXO_TEST(inf_limit_hubs_6, { CHECK_INF("BINF AAAB NIFriend IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y HR1\n", status_msg_user_hub_limit_low); }); +EXO_TEST(inf_limit_hubs_7, { CHECK_INF("BINF AAAB NIFriend IDGNSSMURMD7K466NGZIHU65TP3S3UZSQ6MN5B2RI PD3A4545WFVGZLSGUXZLG7OS6ULQUVG3HM2T63I7Y HN15 HR15 HO15\n", status_msg_user_hub_limit_high); }); + + +EXO_TEST(inf_destroy_setup, +{ + inf_destroy_user(); + inf_destroy_hub(); + return 1; +}); diff --git a/autotest/test_ipfilter.tcc b/autotest/test_ipfilter.tcc new file mode 100644 index 0000000..0bb4e4e --- /dev/null +++ b/autotest/test_ipfilter.tcc @@ -0,0 +1,599 @@ +#include + +static int ipv6 = 0; + +static struct ip_addr_encap ip4_a; +static struct ip_addr_encap ip4_b; +static struct ip_addr_encap ip4_c; +static struct ip_addr_encap ip6_a; +static struct ip_addr_encap ip6_b; +static struct ip_addr_encap ip6_c; +static struct ip_addr_encap mask; +static struct ip_ban_record ban6; +static struct ip_ban_record ban4; + +EXO_TEST(prepare_network, { + return net_initialize() == 0; +}); + +EXO_TEST(check_ipv6, { + ipv6 = net_is_ipv6_supported(); + return ipv6 != -1; +}); + +EXO_TEST(create_addresses_1, { + return + ip_convert_to_binary("192.168.0.0", &ip4_a) && + ip_convert_to_binary("192.168.255.255", &ip4_b) && + ip_convert_to_binary("192.168.0.1", &ip4_c); +}); + +EXO_TEST(create_addresses_2, { + return + ip_convert_to_binary("2001::201:2ff:fefa:0", &ip6_a) && + ip_convert_to_binary("2001::201:2ff:fefa:ffff", &ip6_b) && + ip_convert_to_binary("2001::201:2ff:fefa:fffe", &ip6_c); +}); + +EXO_TEST(ip_is_valid_ipv4_1, { + return ip_is_valid_ipv4("127.0.0.1"); +}); + +EXO_TEST(ip_is_valid_ipv4_2, { + return ip_is_valid_ipv4("10.18.1.178"); +}); + +EXO_TEST(ip_is_valid_ipv4_3, { + return ip_is_valid_ipv4("10.18.1.178"); +}); + +EXO_TEST(ip_is_valid_ipv4_4, { + return ip_is_valid_ipv4("224.0.0.1"); +}); + +EXO_TEST(ip_is_valid_ipv4_5, { + return !ip_is_valid_ipv4("224.0.0."); +}); + +EXO_TEST(ip_is_valid_ipv4_6, { + return !ip_is_valid_ipv4("invalid"); +}); + +EXO_TEST(ip_is_valid_ipv4_7, { + return !ip_is_valid_ipv4("localhost"); +}); + +EXO_TEST(ip_is_valid_ipv4_8, { + return !ip_is_valid_ipv4("123.45.67.890"); +}); + +EXO_TEST(ip_is_valid_ipv4_9, { + return !ip_is_valid_ipv4("777.777.777.777"); +}); + +EXO_TEST(ip_is_valid_ipv6_1, { + if (!ipv6) return 1; + return ip_is_valid_ipv6("::"); +}); + +EXO_TEST(ip_is_valid_ipv6_2, { + if (!ipv6) return 1; + return ip_is_valid_ipv6("::1"); +}); + +EXO_TEST(ip_is_valid_ipv6_3, { + if (!ipv6) return 1; + return ip_is_valid_ipv6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); +}); + +EXO_TEST(ip4_compare_1, { + return ip_compare(&ip4_a, &ip4_b) < 0; +}); + +EXO_TEST(ip4_compare_2, { + return ip_compare(&ip4_a, &ip4_c) < 0; +}); + +EXO_TEST(ip4_compare_3, { + return ip_compare(&ip4_b, &ip4_c) > 0; +}); + +EXO_TEST(ip4_compare_4, { + return ip_compare(&ip4_b, &ip4_a) > 0; +}); + +EXO_TEST(ip4_compare_5, { + return ip_compare(&ip4_c, &ip4_a) > 0; +}); + +EXO_TEST(ip4_compare_6, { + if (!ipv6) return 1; + return ip_compare(&ip4_c, &ip4_c) == 0; +}); + +EXO_TEST(ip6_compare_1, { + if (!ipv6) return 1; + return ip_compare(&ip6_a, &ip6_b) < 0; +}); + +EXO_TEST(ip6_compare_2, { + if (!ipv6) return 1; + return ip_compare(&ip6_a, &ip6_c) < 0; +}); + +EXO_TEST(ip6_compare_3, { + if (!ipv6) return 1; + return ip_compare(&ip6_b, &ip6_c) > 0; +}); + +EXO_TEST(ip6_compare_4, { + if (!ipv6) return 1; + return ip_compare(&ip6_b, &ip6_a) > 0; +}); + +EXO_TEST(ip6_compare_5, { + if (!ipv6) return 1; + return ip_compare(&ip6_c, &ip6_a) > 0; +}); + +EXO_TEST(ip6_compare_6, { + if (!ipv6) return 1; + return ip_compare(&ip6_c, &ip6_c) == 0; +}); + +static int compare_str(const char* s1, const char* s2) +{ + int ok = strcmp(s1, s2); +#ifdef DEBUG_TESTS + if (ok) + { + printf("compare_str fail: s1='%s', s2='%s'\n", s1, s2); + } +#endif + return ok; +} + +#define LMASK4(bits) !ip_mask_create_left (AF_INET, bits, &mask) +#define LMASK6(bits) (ipv6 ? !ip_mask_create_left (AF_INET6, bits, &mask) : 1) +#define RMASK4(bits) !ip_mask_create_right(AF_INET, bits, &mask) +#define RMASK6(bits) (ipv6 ? !ip_mask_create_right(AF_INET6, bits, &mask) : 1) +#define CHECK4(expect) !compare_str(ip_convert_to_string(&mask), expect) +#define CHECK6(expect) (ipv6 ? !compare_str(ip_convert_to_string(&mask), expect) : 1) + +/* Check IPv4 masks */ +EXO_TEST(ipv4_lmask_create_0, { return LMASK4( 0) && CHECK4("0.0.0.0"); }); +EXO_TEST(ipv4_lmask_create_1, { return LMASK4( 1) && CHECK4("128.0.0.0"); }); +EXO_TEST(ipv4_lmask_create_2, { return LMASK4( 2) && CHECK4("192.0.0.0"); }); +EXO_TEST(ipv4_lmask_create_3, { return LMASK4( 3) && CHECK4("224.0.0.0"); }); +EXO_TEST(ipv4_lmask_create_4, { return LMASK4( 4) && CHECK4("240.0.0.0"); }); +EXO_TEST(ipv4_lmask_create_5, { return LMASK4( 5) && CHECK4("248.0.0.0"); }); +EXO_TEST(ipv4_lmask_create_6, { return LMASK4( 6) && CHECK4("252.0.0.0"); }); +EXO_TEST(ipv4_lmask_create_7, { return LMASK4( 7) && CHECK4("254.0.0.0"); }); +EXO_TEST(ipv4_lmask_create_8, { return LMASK4( 8) && CHECK4("255.0.0.0"); }); +EXO_TEST(ipv4_lmask_create_9, { return LMASK4( 9) && CHECK4("255.128.0.0"); }); +EXO_TEST(ipv4_lmask_create_10, { return LMASK4(10) && CHECK4("255.192.0.0"); }); +EXO_TEST(ipv4_lmask_create_11, { return LMASK4(11) && CHECK4("255.224.0.0"); }); +EXO_TEST(ipv4_lmask_create_12, { return LMASK4(12) && CHECK4("255.240.0.0"); }); +EXO_TEST(ipv4_lmask_create_13, { return LMASK4(13) && CHECK4("255.248.0.0"); }); +EXO_TEST(ipv4_lmask_create_14, { return LMASK4(14) && CHECK4("255.252.0.0"); }); +EXO_TEST(ipv4_lmask_create_15, { return LMASK4(15) && CHECK4("255.254.0.0"); }); +EXO_TEST(ipv4_lmask_create_16, { return LMASK4(16) && CHECK4("255.255.0.0"); }); +EXO_TEST(ipv4_lmask_create_17, { return LMASK4(17) && CHECK4("255.255.128.0"); }); +EXO_TEST(ipv4_lmask_create_18, { return LMASK4(18) && CHECK4("255.255.192.0"); }); +EXO_TEST(ipv4_lmask_create_19, { return LMASK4(19) && CHECK4("255.255.224.0"); }); +EXO_TEST(ipv4_lmask_create_20, { return LMASK4(20) && CHECK4("255.255.240.0"); }); +EXO_TEST(ipv4_lmask_create_21, { return LMASK4(21) && CHECK4("255.255.248.0"); }); +EXO_TEST(ipv4_lmask_create_22, { return LMASK4(22) && CHECK4("255.255.252.0"); }); +EXO_TEST(ipv4_lmask_create_23, { return LMASK4(23) && CHECK4("255.255.254.0"); }); +EXO_TEST(ipv4_lmask_create_24, { return LMASK4(24) && CHECK4("255.255.255.0"); }); +EXO_TEST(ipv4_lmask_create_25, { return LMASK4(25) && CHECK4("255.255.255.128"); }); +EXO_TEST(ipv4_lmask_create_26, { return LMASK4(26) && CHECK4("255.255.255.192"); }); +EXO_TEST(ipv4_lmask_create_27, { return LMASK4(27) && CHECK4("255.255.255.224"); }); +EXO_TEST(ipv4_lmask_create_28, { return LMASK4(28) && CHECK4("255.255.255.240"); }); +EXO_TEST(ipv4_lmask_create_29, { return LMASK4(29) && CHECK4("255.255.255.248"); }); +EXO_TEST(ipv4_lmask_create_30, { return LMASK4(30) && CHECK4("255.255.255.252"); }); +EXO_TEST(ipv4_lmask_create_31, { return LMASK4(31) && CHECK4("255.255.255.254"); }); +EXO_TEST(ipv4_lmask_create_32, { return LMASK4(32) && CHECK4("255.255.255.255"); }); + +/* Check IPv4 right to left mask */ +EXO_TEST(ipv4_rmask_create_0, { return RMASK4( 0) && CHECK4("0.0.0.0"); }); +EXO_TEST(ipv4_rmask_create_1, { return RMASK4( 1) && CHECK4("0.0.0.1"); }); +EXO_TEST(ipv4_rmask_create_2, { return RMASK4( 2) && CHECK4("0.0.0.3"); }); +EXO_TEST(ipv4_rmask_create_3, { return RMASK4( 3) && CHECK4("0.0.0.7"); }); +EXO_TEST(ipv4_rmask_create_4, { return RMASK4( 4) && CHECK4("0.0.0.15"); }); +EXO_TEST(ipv4_rmask_create_5, { return RMASK4( 5) && CHECK4("0.0.0.31"); }); +EXO_TEST(ipv4_rmask_create_6, { return RMASK4( 6) && CHECK4("0.0.0.63"); }); +EXO_TEST(ipv4_rmask_create_7, { return RMASK4( 7) && CHECK4("0.0.0.127"); }); +EXO_TEST(ipv4_rmask_create_8, { return RMASK4( 8) && CHECK4("0.0.0.255"); }); +EXO_TEST(ipv4_rmask_create_9, { return RMASK4( 9) && CHECK4("0.0.1.255"); }); +EXO_TEST(ipv4_rmask_create_10, { return RMASK4(10) && CHECK4("0.0.3.255"); }); +EXO_TEST(ipv4_rmask_create_11, { return RMASK4(11) && CHECK4("0.0.7.255"); }); +EXO_TEST(ipv4_rmask_create_12, { return RMASK4(12) && CHECK4("0.0.15.255"); }); +EXO_TEST(ipv4_rmask_create_13, { return RMASK4(13) && CHECK4("0.0.31.255"); }); +EXO_TEST(ipv4_rmask_create_14, { return RMASK4(14) && CHECK4("0.0.63.255"); }); +EXO_TEST(ipv4_rmask_create_15, { return RMASK4(15) && CHECK4("0.0.127.255"); }); +EXO_TEST(ipv4_rmask_create_16, { return RMASK4(16) && CHECK4("0.0.255.255"); }); +EXO_TEST(ipv4_rmask_create_17, { return RMASK4(17) && CHECK4("0.1.255.255"); }); +EXO_TEST(ipv4_rmask_create_18, { return RMASK4(18) && CHECK4("0.3.255.255"); }); +EXO_TEST(ipv4_rmask_create_19, { return RMASK4(19) && CHECK4("0.7.255.255"); }); +EXO_TEST(ipv4_rmask_create_20, { return RMASK4(20) && CHECK4("0.15.255.255"); }); +EXO_TEST(ipv4_rmask_create_21, { return RMASK4(21) && CHECK4("0.31.255.255"); }); +EXO_TEST(ipv4_rmask_create_22, { return RMASK4(22) && CHECK4("0.63.255.255"); }); +EXO_TEST(ipv4_rmask_create_23, { return RMASK4(23) && CHECK4("0.127.255.255"); }); +EXO_TEST(ipv4_rmask_create_24, { return RMASK4(24) && CHECK4("0.255.255.255"); }); +EXO_TEST(ipv4_rmask_create_25, { return RMASK4(25) && CHECK4("1.255.255.255"); }); +EXO_TEST(ipv4_rmask_create_26, { return RMASK4(26) && CHECK4("3.255.255.255"); }); +EXO_TEST(ipv4_rmask_create_27, { return RMASK4(27) && CHECK4("7.255.255.255"); }); +EXO_TEST(ipv4_rmask_create_28, { return RMASK4(28) && CHECK4("15.255.255.255"); }); +EXO_TEST(ipv4_rmask_create_29, { return RMASK4(29) && CHECK4("31.255.255.255"); }); +EXO_TEST(ipv4_rmask_create_30, { return RMASK4(30) && CHECK4("63.255.255.255"); }); +EXO_TEST(ipv4_rmask_create_31, { return RMASK4(31) && CHECK4("127.255.255.255"); }); +EXO_TEST(ipv4_rmask_create_32, { return RMASK4(32) && CHECK4("255.255.255.255"); }); + + +/* Check IPv6 masks */ +EXO_TEST(ip6_lmask_create_0, { return LMASK6( 0) && CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); }); +EXO_TEST(ip6_lmask_create_1, { return LMASK6( 1) && CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe"); }); +EXO_TEST(ip6_lmask_create_2, { return LMASK6( 2) && CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffc"); }); +EXO_TEST(ip6_lmask_create_3, { return LMASK6( 3) && CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff8"); }); +EXO_TEST(ip6_lmask_create_4, { return LMASK6( 4) && CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff0"); }); +EXO_TEST(ip6_lmask_create_5, { return LMASK6( 5) && CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffe0"); }); +EXO_TEST(ip6_lmask_create_6, { return LMASK6( 6) && CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffc0"); }); +EXO_TEST(ip6_lmask_create_7, { return LMASK6( 7) && CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff80"); }); +EXO_TEST(ip6_lmask_create_8, { return LMASK6( 8) && CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00"); }); +EXO_TEST(ip6_lmask_create_9, { return LMASK6( 9) && CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fe00"); }); +EXO_TEST(ip6_lmask_create_10, { return LMASK6( 10) && CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fc00"); }); +EXO_TEST(ip6_lmask_create_11, { return LMASK6( 11) && CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:f800"); }); +EXO_TEST(ip6_lmask_create_12, { return LMASK6( 12) && CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:f000"); }); +EXO_TEST(ip6_lmask_create_13, { return LMASK6( 13) && CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:e000"); }); +EXO_TEST(ip6_lmask_create_14, { return LMASK6( 14) && CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:c000"); }); +EXO_TEST(ip6_lmask_create_15, { return LMASK6( 15) && CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:8000"); }); +EXO_TEST(ip6_lmask_create_16, { return LMASK6( 16) && (CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:0") || CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:ffff::")); }); +EXO_TEST(ip6_lmask_create_17, { return LMASK6( 17) && (CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:fffe:0") || CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:fffe::")); }); +EXO_TEST(ip6_lmask_create_18, { return LMASK6( 18) && (CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:fffc:0") || CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:fffc::")); }); +EXO_TEST(ip6_lmask_create_19, { return LMASK6( 19) && (CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:fff8:0") || CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:fff8::")); }); +EXO_TEST(ip6_lmask_create_20, { return LMASK6( 20) && (CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:fff0:0") || CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:fff0::")); }); +EXO_TEST(ip6_lmask_create_21, { return LMASK6( 21) && (CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:ffe0:0") || CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:ffe0::")); }); +EXO_TEST(ip6_lmask_create_22, { return LMASK6( 22) && (CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:ffc0:0") || CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:ffc0::")); }); +EXO_TEST(ip6_lmask_create_23, { return LMASK6( 23) && (CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:ff80:0") || CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:ff80::")); }); +EXO_TEST(ip6_lmask_create_24, { return LMASK6( 24) && (CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:ff00:0") || CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:ff00::")); }); +EXO_TEST(ip6_lmask_create_25, { return LMASK6( 25) && (CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:fe00:0") || CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:fe00::")); }); +EXO_TEST(ip6_lmask_create_26, { return LMASK6( 26) && (CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:fc00:0") || CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:fc00::")); }); +EXO_TEST(ip6_lmask_create_27, { return LMASK6( 27) && (CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:f800:0") || CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:f800::")); }); +EXO_TEST(ip6_lmask_create_28, { return LMASK6( 28) && (CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:f000:0") || CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:f000::")); }); +EXO_TEST(ip6_lmask_create_29, { return LMASK6( 29) && (CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:e000:0") || CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:e000::")); }); +EXO_TEST(ip6_lmask_create_30, { return LMASK6( 30) && (CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:c000:0") || CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:c000::")); }); +EXO_TEST(ip6_lmask_create_31, { return LMASK6( 31) && (CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:8000:0") || CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:8000::")); }); +EXO_TEST(ip6_lmask_create_32, { return LMASK6( 32) && CHECK6("ffff:ffff:ffff:ffff:ffff:ffff::"); }); +EXO_TEST(ip6_lmask_create_33, { return LMASK6( 33) && CHECK6("ffff:ffff:ffff:ffff:ffff:fffe::"); }); +EXO_TEST(ip6_lmask_create_34, { return LMASK6( 34) && CHECK6("ffff:ffff:ffff:ffff:ffff:fffc::"); }); +EXO_TEST(ip6_lmask_create_35, { return LMASK6( 35) && CHECK6("ffff:ffff:ffff:ffff:ffff:fff8::"); }); +EXO_TEST(ip6_lmask_create_36, { return LMASK6( 36) && CHECK6("ffff:ffff:ffff:ffff:ffff:fff0::"); }); +EXO_TEST(ip6_lmask_create_37, { return LMASK6( 37) && CHECK6("ffff:ffff:ffff:ffff:ffff:ffe0::"); }); +EXO_TEST(ip6_lmask_create_38, { return LMASK6( 38) && CHECK6("ffff:ffff:ffff:ffff:ffff:ffc0::"); }); +EXO_TEST(ip6_lmask_create_39, { return LMASK6( 39) && CHECK6("ffff:ffff:ffff:ffff:ffff:ff80::"); }); +EXO_TEST(ip6_lmask_create_40, { return LMASK6( 40) && CHECK6("ffff:ffff:ffff:ffff:ffff:ff00::"); }); +EXO_TEST(ip6_lmask_create_41, { return LMASK6( 41) && CHECK6("ffff:ffff:ffff:ffff:ffff:fe00::"); }); +EXO_TEST(ip6_lmask_create_42, { return LMASK6( 42) && CHECK6("ffff:ffff:ffff:ffff:ffff:fc00::"); }); +EXO_TEST(ip6_lmask_create_43, { return LMASK6( 43) && CHECK6("ffff:ffff:ffff:ffff:ffff:f800::"); }); +EXO_TEST(ip6_lmask_create_44, { return LMASK6( 44) && CHECK6("ffff:ffff:ffff:ffff:ffff:f000::"); }); +EXO_TEST(ip6_lmask_create_45, { return LMASK6( 45) && CHECK6("ffff:ffff:ffff:ffff:ffff:e000::"); }); +EXO_TEST(ip6_lmask_create_46, { return LMASK6( 46) && CHECK6("ffff:ffff:ffff:ffff:ffff:c000::"); }); +EXO_TEST(ip6_lmask_create_47, { return LMASK6( 47) && CHECK6("ffff:ffff:ffff:ffff:ffff:8000::"); }); +EXO_TEST(ip6_lmask_create_48, { return LMASK6( 48) && CHECK6("ffff:ffff:ffff:ffff:ffff::"); }); +EXO_TEST(ip6_lmask_create_49, { return LMASK6( 49) && CHECK6("ffff:ffff:ffff:ffff:fffe::"); }); +EXO_TEST(ip6_lmask_create_50, { return LMASK6( 50) && CHECK6("ffff:ffff:ffff:ffff:fffc::"); }); +EXO_TEST(ip6_lmask_create_51, { return LMASK6( 51) && CHECK6("ffff:ffff:ffff:ffff:fff8::"); }); +EXO_TEST(ip6_lmask_create_52, { return LMASK6( 52) && CHECK6("ffff:ffff:ffff:ffff:fff0::"); }); +EXO_TEST(ip6_lmask_create_53, { return LMASK6( 53) && CHECK6("ffff:ffff:ffff:ffff:ffe0::"); }); +EXO_TEST(ip6_lmask_create_54, { return LMASK6( 54) && CHECK6("ffff:ffff:ffff:ffff:ffc0::"); }); +EXO_TEST(ip6_lmask_create_55, { return LMASK6( 55) && CHECK6("ffff:ffff:ffff:ffff:ff80::"); }); +EXO_TEST(ip6_lmask_create_56, { return LMASK6( 56) && CHECK6("ffff:ffff:ffff:ffff:ff00::"); }); +EXO_TEST(ip6_lmask_create_57, { return LMASK6( 57) && CHECK6("ffff:ffff:ffff:ffff:fe00::"); }); +EXO_TEST(ip6_lmask_create_58, { return LMASK6( 58) && CHECK6("ffff:ffff:ffff:ffff:fc00::"); }); +EXO_TEST(ip6_lmask_create_59, { return LMASK6( 59) && CHECK6("ffff:ffff:ffff:ffff:f800::"); }); +EXO_TEST(ip6_lmask_create_60, { return LMASK6( 60) && CHECK6("ffff:ffff:ffff:ffff:f000::"); }); +EXO_TEST(ip6_lmask_create_61, { return LMASK6( 61) && CHECK6("ffff:ffff:ffff:ffff:e000::"); }); +EXO_TEST(ip6_lmask_create_62, { return LMASK6( 62) && CHECK6("ffff:ffff:ffff:ffff:c000::"); }); +EXO_TEST(ip6_lmask_create_63, { return LMASK6( 63) && CHECK6("ffff:ffff:ffff:ffff:8000::"); }); +EXO_TEST(ip6_lmask_create_64, { return LMASK6( 64) && CHECK6("ffff:ffff:ffff:ffff::"); }); +EXO_TEST(ip6_lmask_create_65, { return LMASK6( 65) && CHECK6("ffff:ffff:ffff:fffe::"); }); +EXO_TEST(ip6_lmask_create_66, { return LMASK6( 66) && CHECK6("ffff:ffff:ffff:fffc::"); }); +EXO_TEST(ip6_lmask_create_67, { return LMASK6( 67) && CHECK6("ffff:ffff:ffff:fff8::"); }); +EXO_TEST(ip6_lmask_create_68, { return LMASK6( 68) && CHECK6("ffff:ffff:ffff:fff0::"); }); +EXO_TEST(ip6_lmask_create_69, { return LMASK6( 69) && CHECK6("ffff:ffff:ffff:ffe0::"); }); +EXO_TEST(ip6_lmask_create_70, { return LMASK6( 70) && CHECK6("ffff:ffff:ffff:ffc0::"); }); +EXO_TEST(ip6_lmask_create_71, { return LMASK6( 71) && CHECK6("ffff:ffff:ffff:ff80::"); }); +EXO_TEST(ip6_lmask_create_72, { return LMASK6( 72) && CHECK6("ffff:ffff:ffff:ff00::"); }); +EXO_TEST(ip6_lmask_create_73, { return LMASK6( 73) && CHECK6("ffff:ffff:ffff:fe00::"); }); +EXO_TEST(ip6_lmask_create_74, { return LMASK6( 74) && CHECK6("ffff:ffff:ffff:fc00::"); }); +EXO_TEST(ip6_lmask_create_75, { return LMASK6( 75) && CHECK6("ffff:ffff:ffff:f800::"); }); +EXO_TEST(ip6_lmask_create_76, { return LMASK6( 76) && CHECK6("ffff:ffff:ffff:f000::"); }); +EXO_TEST(ip6_lmask_create_77, { return LMASK6( 77) && CHECK6("ffff:ffff:ffff:e000::"); }); +EXO_TEST(ip6_lmask_create_78, { return LMASK6( 78) && CHECK6("ffff:ffff:ffff:c000::"); }); +EXO_TEST(ip6_lmask_create_79, { return LMASK6( 79) && CHECK6("ffff:ffff:ffff:8000::"); }); +EXO_TEST(ip6_lmask_create_80, { return LMASK6( 80) && CHECK6("ffff:ffff:ffff::"); }); +EXO_TEST(ip6_lmask_create_81, { return LMASK6( 81) && CHECK6("ffff:ffff:fffe::"); }); +EXO_TEST(ip6_lmask_create_82, { return LMASK6( 82) && CHECK6("ffff:ffff:fffc::"); }); +EXO_TEST(ip6_lmask_create_83, { return LMASK6( 83) && CHECK6("ffff:ffff:fff8::"); }); +EXO_TEST(ip6_lmask_create_84, { return LMASK6( 84) && CHECK6("ffff:ffff:fff0::"); }); +EXO_TEST(ip6_lmask_create_85, { return LMASK6( 85) && CHECK6("ffff:ffff:ffe0::"); }); +EXO_TEST(ip6_lmask_create_86, { return LMASK6( 86) && CHECK6("ffff:ffff:ffc0::"); }); +EXO_TEST(ip6_lmask_create_87, { return LMASK6( 87) && CHECK6("ffff:ffff:ff80::"); }); +EXO_TEST(ip6_lmask_create_88, { return LMASK6( 88) && CHECK6("ffff:ffff:ff00::"); }); +EXO_TEST(ip6_lmask_create_89, { return LMASK6( 89) && CHECK6("ffff:ffff:fe00::"); }); +EXO_TEST(ip6_lmask_create_90, { return LMASK6( 90) && CHECK6("ffff:ffff:fc00::"); }); +EXO_TEST(ip6_lmask_create_91, { return LMASK6( 91) && CHECK6("ffff:ffff:f800::"); }); +EXO_TEST(ip6_lmask_create_92, { return LMASK6( 92) && CHECK6("ffff:ffff:f000::"); }); +EXO_TEST(ip6_lmask_create_93, { return LMASK6( 93) && CHECK6("ffff:ffff:e000::"); }); +EXO_TEST(ip6_lmask_create_94, { return LMASK6( 94) && CHECK6("ffff:ffff:c000::"); }); +EXO_TEST(ip6_lmask_create_95, { return LMASK6( 95) && CHECK6("ffff:ffff:8000::"); }); +EXO_TEST(ip6_lmask_create_96, { return LMASK6( 96) && CHECK6("ffff:ffff::"); }); +EXO_TEST(ip6_lmask_create_97, { return LMASK6( 97) && CHECK6("ffff:fffe::"); }); +EXO_TEST(ip6_lmask_create_98, { return LMASK6( 98) && CHECK6("ffff:fffc::"); }); +EXO_TEST(ip6_lmask_create_99, { return LMASK6( 99) && CHECK6("ffff:fff8::"); }); +EXO_TEST(ip6_lmask_create_100, { return LMASK6(100) && CHECK6("ffff:fff0::"); }); +EXO_TEST(ip6_lmask_create_101, { return LMASK6(101) && CHECK6("ffff:ffe0::"); }); +EXO_TEST(ip6_lmask_create_102, { return LMASK6(102) && CHECK6("ffff:ffc0::"); }); +EXO_TEST(ip6_lmask_create_103, { return LMASK6(103) && CHECK6("ffff:ff80::"); }); +EXO_TEST(ip6_lmask_create_104, { return LMASK6(104) && CHECK6("ffff:ff00::"); }); +EXO_TEST(ip6_lmask_create_105, { return LMASK6(105) && CHECK6("ffff:fe00::"); }); +EXO_TEST(ip6_lmask_create_106, { return LMASK6(106) && CHECK6("ffff:fc00::"); }); +EXO_TEST(ip6_lmask_create_107, { return LMASK6(107) && CHECK6("ffff:f800::"); }); +EXO_TEST(ip6_lmask_create_108, { return LMASK6(108) && CHECK6("ffff:f000::"); }); +EXO_TEST(ip6_lmask_create_109, { return LMASK6(109) && CHECK6("ffff:e000::"); }); +EXO_TEST(ip6_lmask_create_110, { return LMASK6(110) && CHECK6("ffff:c000::"); }); +EXO_TEST(ip6_lmask_create_111, { return LMASK6(111) && CHECK6("ffff:8000::"); }); +EXO_TEST(ip6_lmask_create_112, { return LMASK6(112) && CHECK6("ffff::"); }); +EXO_TEST(ip6_lmask_create_113, { return LMASK6(113) && CHECK6("fffe::"); }); +EXO_TEST(ip6_lmask_create_114, { return LMASK6(114) && CHECK6("fffc::"); }); +EXO_TEST(ip6_lmask_create_115, { return LMASK6(115) && CHECK6("fff8::"); }); +EXO_TEST(ip6_lmask_create_116, { return LMASK6(116) && CHECK6("fff0::"); }); +EXO_TEST(ip6_lmask_create_117, { return LMASK6(117) && CHECK6("ffe0::"); }); +EXO_TEST(ip6_lmask_create_118, { return LMASK6(118) && CHECK6("ffc0::"); }); +EXO_TEST(ip6_lmask_create_119, { return LMASK6(119) && CHECK6("ff80::"); }); +EXO_TEST(ip6_lmask_create_120, { return LMASK6(120) && CHECK6("ff00::"); }); +EXO_TEST(ip6_lmask_create_121, { return LMASK6(121) && CHECK6("fe00::"); }); +EXO_TEST(ip6_lmask_create_122, { return LMASK6(122) && CHECK6("fc00::"); }); +EXO_TEST(ip6_lmask_create_123, { return LMASK6(123) && CHECK6("f800::"); }); +EXO_TEST(ip6_lmask_create_124, { return LMASK6(124) && CHECK6("f000::"); }); +EXO_TEST(ip6_lmask_create_125, { return LMASK6(125) && CHECK6("e000::"); }); +EXO_TEST(ip6_lmask_create_126, { return LMASK6(126) && CHECK6("c000::"); }); +EXO_TEST(ip6_lmask_create_127, { return LMASK6(127) && CHECK6("8000::"); }); +EXO_TEST(ip6_lmask_create_128, { return LMASK6(128) && CHECK6("::"); }); + +/* Check IPv6 right to left masks */ +EXO_TEST(ip6_rmask_create_0, { return RMASK6( 0) && CHECK6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); }); +EXO_TEST(ip6_rmask_create_1, { return RMASK6( 1) && CHECK6("7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); }); +EXO_TEST(ip6_rmask_create_2, { return RMASK6( 2) && CHECK6("3fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); }); +EXO_TEST(ip6_rmask_create_3, { return RMASK6( 3) && CHECK6("1fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); }); +EXO_TEST(ip6_rmask_create_4, { return RMASK6( 4) && CHECK6("fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); }); +EXO_TEST(ip6_rmask_create_5, { return RMASK6( 5) && CHECK6("7ff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); }); +EXO_TEST(ip6_rmask_create_6, { return RMASK6( 6) && CHECK6("3ff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); }); +EXO_TEST(ip6_rmask_create_7, { return RMASK6( 7) && CHECK6("1ff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); }); +EXO_TEST(ip6_rmask_create_8, { return RMASK6( 8) && CHECK6("ff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); }); +EXO_TEST(ip6_rmask_create_9, { return RMASK6( 9) && CHECK6("7f:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); }); +EXO_TEST(ip6_rmask_create_10, { return RMASK6( 10) && CHECK6("3f:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); }); +EXO_TEST(ip6_rmask_create_11, { return RMASK6( 11) && CHECK6("1f:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); }); +EXO_TEST(ip6_rmask_create_12, { return RMASK6( 12) && CHECK6("f:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); }); +EXO_TEST(ip6_rmask_create_13, { return RMASK6( 13) && CHECK6("7:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); }); +EXO_TEST(ip6_rmask_create_14, { return RMASK6( 14) && CHECK6("3:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); }); +EXO_TEST(ip6_rmask_create_15, { return RMASK6( 15) && CHECK6("1:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); }); +EXO_TEST(ip6_rmask_create_16, { return RMASK6( 16) && (CHECK6("0:ffff:ffff:ffff:ffff:ffff:ffff:ffff") || CHECK6("::ffff:ffff:ffff:ffff:ffff:ffff:ffff") || CHECK6("ffff:ffff:ffff:ffff:ffff:ffff")); }); +EXO_TEST(ip6_rmask_create_17, { return RMASK6( 17) && (CHECK6("0:7fff:ffff:ffff:ffff:ffff:ffff:ffff") || CHECK6("::7fff:ffff:ffff:ffff:ffff:ffff:ffff")); }); +EXO_TEST(ip6_rmask_create_18, { return RMASK6( 18) && (CHECK6("0:3fff:ffff:ffff:ffff:ffff:ffff:ffff") || CHECK6("::3fff:ffff:ffff:ffff:ffff:ffff:ffff")); }); +EXO_TEST(ip6_rmask_create_19, { return RMASK6( 19) && (CHECK6("0:1fff:ffff:ffff:ffff:ffff:ffff:ffff") || CHECK6("::1fff:ffff:ffff:ffff:ffff:ffff:ffff")); }); +EXO_TEST(ip6_rmask_create_20, { return RMASK6( 20) && (CHECK6("0:fff:ffff:ffff:ffff:ffff:ffff:ffff") || CHECK6("::fff:ffff:ffff:ffff:ffff:ffff:ffff")); }); +EXO_TEST(ip6_rmask_create_21, { return RMASK6( 21) && (CHECK6("0:7ff:ffff:ffff:ffff:ffff:ffff:ffff") || CHECK6("::7ff:ffff:ffff:ffff:ffff:ffff:ffff")); }); +EXO_TEST(ip6_rmask_create_22, { return RMASK6( 22) && (CHECK6("0:3ff:ffff:ffff:ffff:ffff:ffff:ffff") || CHECK6("::3ff:ffff:ffff:ffff:ffff:ffff:ffff")); }); +EXO_TEST(ip6_rmask_create_23, { return RMASK6( 23) && (CHECK6("0:1ff:ffff:ffff:ffff:ffff:ffff:ffff") || CHECK6("::1ff:ffff:ffff:ffff:ffff:ffff:ffff")); }); +EXO_TEST(ip6_rmask_create_24, { return RMASK6( 24) && (CHECK6("0:ff:ffff:ffff:ffff:ffff:ffff:ffff") || CHECK6("::ff:ffff:ffff:ffff:ffff:ffff:ffff")); }); +EXO_TEST(ip6_rmask_create_25, { return RMASK6( 25) && (CHECK6("0:7f:ffff:ffff:ffff:ffff:ffff:ffff") || CHECK6("::7f:ffff:ffff:ffff:ffff:ffff:ffff")); }); +EXO_TEST(ip6_rmask_create_26, { return RMASK6( 26) && (CHECK6("0:3f:ffff:ffff:ffff:ffff:ffff:ffff") || CHECK6("::3f:ffff:ffff:ffff:ffff:ffff:ffff")); }); +EXO_TEST(ip6_rmask_create_27, { return RMASK6( 27) && (CHECK6("0:1f:ffff:ffff:ffff:ffff:ffff:ffff") || CHECK6("::1f:ffff:ffff:ffff:ffff:ffff:ffff")); }); +EXO_TEST(ip6_rmask_create_28, { return RMASK6( 28) && (CHECK6("0:f:ffff:ffff:ffff:ffff:ffff:ffff") || CHECK6("::f:ffff:ffff:ffff:ffff:ffff:ffff")); }); +EXO_TEST(ip6_rmask_create_29, { return RMASK6( 29) && (CHECK6("0:7:ffff:ffff:ffff:ffff:ffff:ffff") || CHECK6("::7:ffff:ffff:ffff:ffff:ffff:ffff")); }); +EXO_TEST(ip6_rmask_create_30, { return RMASK6( 30) && (CHECK6("0:3:ffff:ffff:ffff:ffff:ffff:ffff") || CHECK6("::3:ffff:ffff:ffff:ffff:ffff:ffff")); }); +EXO_TEST(ip6_rmask_create_31, { return RMASK6( 31) && (CHECK6("0:1:ffff:ffff:ffff:ffff:ffff:ffff") || CHECK6("::1:ffff:ffff:ffff:ffff:ffff:ffff")); }); + +EXO_TEST(check_ban_setup_1, { + return ip_convert_to_binary("2001::201:2ff:fefa:0", &ban6.lo) && + ip_convert_to_binary("2001::201:2ff:fefa:ffff", &ban6.hi) && + ip_convert_to_binary("192.168.0.0", &ban4.lo) && + ip_convert_to_binary("192.168.0.255", &ban4.hi); +}); + +EXO_TEST(check_ban_ipv4_1, { + struct ip_addr_encap addr; ip_convert_to_binary("192.168.0.0", &addr); + return acl_check_ip_range(&addr, &ban4); +}); + +EXO_TEST(check_ban_ipv4_2, { + struct ip_addr_encap addr; ip_convert_to_binary("192.168.0.1", &addr); + return acl_check_ip_range(&addr, &ban4); +}); + +EXO_TEST(check_ban_ipv4_3, { + struct ip_addr_encap addr; ip_convert_to_binary("192.168.0.255", &addr); + return acl_check_ip_range(&addr, &ban4); +}); + +EXO_TEST(check_ban_ipv4_4, { + struct ip_addr_encap addr; ip_convert_to_binary("192.168.1.0", &addr); + return !acl_check_ip_range(&addr, &ban4); +}); + +EXO_TEST(check_ban_ipv4_5, { + struct ip_addr_encap addr; ip_convert_to_binary("192.167.255.255", &addr); + return !acl_check_ip_range(&addr, &ban4); +}); + +EXO_TEST(check_ban_ipv6_1, { + if (!ipv6) return 1; + struct ip_addr_encap addr; ip_convert_to_binary("2001::201:2ff:fefa:0", &addr); + return acl_check_ip_range(&addr, &ban6); +}); + +EXO_TEST(check_ban_ipv6_2, { + if (!ipv6) return 1; + struct ip_addr_encap addr; ip_convert_to_binary("2001::201:2ff:fefa:1", &addr); + return acl_check_ip_range(&addr, &ban6); +}); + +EXO_TEST(check_ban_ipv6_3, { + if (!ipv6) return 1; + struct ip_addr_encap addr; ip_convert_to_binary("2001::201:2ff:fefa:fffe", &addr); + return acl_check_ip_range(&addr, &ban6); +}); + +EXO_TEST(check_ban_ipv6_4, { + if (!ipv6) return 1; + struct ip_addr_encap addr; ip_convert_to_binary("2001::201:2ff:fefa:ffff", &addr); + return acl_check_ip_range(&addr, &ban6); +}); + +EXO_TEST(check_ban_ipv6_5, { + if (!ipv6) return 1; + struct ip_addr_encap addr; ip_convert_to_binary("2001::201:2ff:fefb:0", &addr); + return !acl_check_ip_range(&addr, &ban6); +}); + +EXO_TEST(check_ban_ipv6_6, { + if (!ipv6) return 1; + struct ip_addr_encap addr; ip_convert_to_binary("2001::201:2ff:fef9:ffff", &addr); + return !acl_check_ip_range(&addr, &ban6); +}); + +EXO_TEST(check_ban_afmix_1, { + if (!ipv6) return 1; + struct ip_addr_encap addr; ip_convert_to_binary("2001::201:2ff:fef9:ffff", &addr); + return !acl_check_ip_range(&addr, &ban4); +}); + +EXO_TEST(check_ban_afmix_2, { + struct ip_addr_encap addr; ip_convert_to_binary("10.20.30.40", &addr); + return !acl_check_ip_range(&addr, &ban6); +}); + +EXO_TEST(ip4_bitwise_AND_1, { + ip_convert_to_binary("255.255.255.255", &ip4_a); + ip_convert_to_binary("255.255.255.0", &ip4_b); + ip_mask_apply_AND(&ip4_a, &ip4_b, &ip4_c); + return !strcmp(ip_convert_to_string(&ip4_c), "255.255.255.0"); +}); + +EXO_TEST(ip4_bitwise_AND_2, { + ip_convert_to_binary("192.168.217.113", &ip4_a); + ip_convert_to_binary("255.255.255.0", &ip4_b); + ip_mask_apply_AND(&ip4_a, &ip4_b, &ip4_c); + return !strcmp(ip_convert_to_string(&ip4_c), "192.168.217.0"); +}); + +EXO_TEST(ip4_bitwise_AND_3, { + ip_convert_to_binary("192.168.217.113", &ip4_a); + ip_convert_to_binary("255.255.0.0", &ip4_b); + ip_mask_apply_AND(&ip4_a, &ip4_b, &ip4_c); + return !strcmp(ip_convert_to_string(&ip4_c), "192.168.0.0"); +}); + +EXO_TEST(ip4_bitwise_AND_4, { + ip_convert_to_binary("192.168.217.113", &ip4_a); + ip_convert_to_binary("255.0.0.0", &ip4_b); + ip_mask_apply_AND(&ip4_a, &ip4_b, &ip4_c); + return !strcmp(ip_convert_to_string(&ip4_c), "192.0.0.0"); +}); + +EXO_TEST(ip4_bitwise_AND_5, { + ip_convert_to_binary("192.168.217.113", &ip4_a); + ip_convert_to_binary("0.0.0.0", &ip4_b); + ip_mask_apply_AND(&ip4_a, &ip4_b, &ip4_c); + return !strcmp(ip_convert_to_string(&ip4_c), "0.0.0.0"); +}); + +EXO_TEST(ip4_bitwise_OR_1, { + ip_convert_to_binary("255.255.255.255", &ip4_a); + ip_convert_to_binary("255.255.255.0", &ip4_b); + ip_mask_apply_OR(&ip4_a, &ip4_b, &ip4_c); + return !strcmp(ip_convert_to_string(&ip4_c), "255.255.255.255"); +}); + +EXO_TEST(ip4_bitwise_OR_2, { + ip_convert_to_binary("192.168.217.113", &ip4_a); + ip_convert_to_binary("255.255.255.0", &ip4_b); + ip_mask_apply_OR(&ip4_a, &ip4_b, &ip4_c); + return !strcmp(ip_convert_to_string(&ip4_c), "255.255.255.113"); +}); + +EXO_TEST(ip4_bitwise_OR_3, { + ip_convert_to_binary("192.168.217.113", &ip4_a); + ip_convert_to_binary("255.255.0.0", &ip4_b); + ip_mask_apply_OR(&ip4_a, &ip4_b, &ip4_c); + return !strcmp(ip_convert_to_string(&ip4_c), "255.255.217.113"); +}); + +EXO_TEST(ip4_bitwise_OR_4, { + ip_convert_to_binary("192.168.217.113", &ip4_a); + ip_convert_to_binary("255.0.0.0", &ip4_b); + ip_mask_apply_OR(&ip4_a, &ip4_b, &ip4_c); + return !strcmp(ip_convert_to_string(&ip4_c), "255.168.217.113"); +}); + +EXO_TEST(ip4_bitwise_OR_5, { + ip_convert_to_binary("192.168.217.113", &ip4_a); + ip_convert_to_binary("0.0.0.0", &ip4_b); + ip_mask_apply_OR(&ip4_a, &ip4_b, &ip4_c); + return !strcmp(ip_convert_to_string(&ip4_c), "192.168.217.113"); +}); + +EXO_TEST(ip6_bitwise_AND_1, { + if (!ipv6) return 1; + ip_convert_to_binary("7f7f:ffff:ffff:3f3f:ffff:ffff:ffff:ffff", &ip6_a); + ip_convert_to_binary("ffff:ffff:ffff:ffff:ffff:ffff:ffff:0", &ip6_b); + ip_mask_apply_AND(&ip6_a, &ip6_b, &ip6_c); + return !strcmp(ip_convert_to_string(&ip6_c), "7f7f:ffff:ffff:3f3f:ffff:ffff:ffff:0") || + !strcmp(ip_convert_to_string(&ip6_c), "7f7f:ffff:ffff:3f3f:ffff:ffff:ffff::"); +}); + +EXO_TEST(ip6_bitwise_AND_2, { + if (!ipv6) return 1; + ip_convert_to_binary("7777:cccc:3333:1111:ffff:ffff:ffff:ffff", &ip6_a); + ip_convert_to_binary("ffff:ffff:ffff:ffff::", &ip6_b); + ip_mask_apply_AND(&ip6_a, &ip6_b, &ip6_c); + return !strcmp(ip_convert_to_string(&ip6_c), "7777:cccc:3333:1111::"); +}); + +EXO_TEST(ip6_bitwise_AND_3, { + if (!ipv6) return 1; + ip_convert_to_binary("7777:cccc:3333:1111:ffff:ffff:ffff:ffff", &ip6_a); + ip_convert_to_binary("::", &ip6_b); + ip_mask_apply_AND(&ip6_a, &ip6_b, &ip6_c); + return !strcmp(ip_convert_to_string(&ip6_c), "::"); +}); + +EXO_TEST(ip6_bitwise_OR_1, { + if (!ipv6) return 1; + ip_convert_to_binary("7f7f:ffff:ffff:3f3f:ffff:ffff:ffff:ffff", &ip6_a); + ip_convert_to_binary("ffff:ffff:ffff:ffff:ffff:ffff:ffff:0", &ip6_b); + ip_mask_apply_OR(&ip6_a, &ip6_b, &ip6_c); + return !strcmp(ip_convert_to_string(&ip6_c), "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); +}); + +EXO_TEST(ip6_bitwise_OR_2, { + if (!ipv6) return 1; + ip_convert_to_binary("7777:cccc:3333:1111:ffff:ffff:ffff:ffff", &ip6_a); + ip_convert_to_binary("ffff:ffff:ffff:ffff:ffff:ffff:ffff:0", &ip6_b); + ip_mask_apply_OR(&ip6_a, &ip6_b, &ip6_c); + return !strcmp(ip_convert_to_string(&ip6_c), "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); +}); + +EXO_TEST(ip6_bitwise_OR_3, { + if (!ipv6) return 1; + ip_convert_to_binary("7777:cccc:3333:1111:ffff:ffff:ffff:1c1c", &ip6_a); + ip_convert_to_binary("::", &ip6_b); + ip_mask_apply_OR(&ip6_a, &ip6_b, &ip6_c); + return !strcmp(ip_convert_to_string(&ip6_c), "7777:cccc:3333:1111:ffff:ffff:ffff:1c1c"); +}); + +EXO_TEST(shutdown_network, { + return net_shutdown() == 0; +}); diff --git a/autotest/test_list.tcc b/autotest/test_list.tcc new file mode 100644 index 0000000..707d18a --- /dev/null +++ b/autotest/test_list.tcc @@ -0,0 +1,137 @@ +#include + +static struct linked_list* list = NULL; + +static char A[2] = { 'A', 0 }; +static char B[2] = { 'B', 0 }; +static char C[2] = { 'C', 0 }; + +static void null_free(void* ptr) +{ + (void) ptr; +} + + +EXO_TEST(list_create_destroy, { + int ok = 0; + struct linked_list* alist; + alist = list_create(); + if (alist) ok = 1; + list_destroy(alist); + return ok; +}); + +EXO_TEST(list_create, { + list = list_create(); + return list->size == 0; +}); + +EXO_TEST(list_append_1, { + list_append(list, (char*) A); + return list->size == 1; +}); + +EXO_TEST(list_remove_1, { + list_remove(list, (char*) A); + return list->size == 0; +}); + + +EXO_TEST(list_append_2, { + list_append(list, A); + list_append(list, B); + return list->size == 2; +}); + +EXO_TEST(list_remove_2, { + list_remove(list, (char*) A); + return list->size == 1; +}); + +EXO_TEST(list_remove_3, { + list_remove(list, (char*) A); /* already removed, so should have no effect */ + return list->size == 1; +}); + +EXO_TEST(list_remove_4, { + list_remove(list, (char*) B); /* already removed, so should have no effect */ + return list->size == 0; +}); + +EXO_TEST(list_append_3, { + list_append(list, A); + list_append(list, B); + list_append(list, C); + return list->size == 3; +}); + +EXO_TEST(list_append_4, { + list_append(list, A); /* OK. adding the same one *AGAIN* */ + return list->size == 4; +}); + +EXO_TEST(list_remove_5, { + list_remove(list, A); /* removing the first one. */ + return list->size == 3; +}); + +EXO_TEST(list_get_index_1, { + return list_get_index(list, 0) == B; +}); + +EXO_TEST(list_get_index_2, { + return list_get_index(list, 1) == C; +}); + +EXO_TEST(list_get_index_3, { + return list_get_index(list, 2) == A; +}); + +EXO_TEST(list_get_index_4, { + return list_get_index(list, 3) == NULL; +}); + +EXO_TEST(list_get_first_1, { + return list_get_first(list) == B; +}); + +EXO_TEST(list_get_first_next_1, { + return list_get_next(list) == C; +}); + +EXO_TEST(list_get_first_next_2, { + return list_get_next(list) == A; +}); + +EXO_TEST(list_get_last_1, { + return list_get_last(list) == A; +}); + +EXO_TEST(list_get_last_prev_1, { + return list_get_prev(list) == C; +}); + +EXO_TEST(list_get_last_prev_2, { + return list_get_prev(list) == B; +}); + +EXO_TEST(list_get_last_prev_next_1, { + return list_get_next(list) == C; +}); + +EXO_TEST(list_clear, { + list_clear(list, &null_free); + return list->size == 0 && list->first == 0 && list->last == 0 && list->iterator == 0; +}); + + +EXO_TEST(list_destroy_1, { + list_destroy(list); + return 1; +}); + +EXO_TEST(list_destroy_2, { + list_destroy(0); + return 1; +}); + diff --git a/autotest/test_memory.tcc b/autotest/test_memory.tcc new file mode 100644 index 0000000..c2fa3e9 --- /dev/null +++ b/autotest/test_memory.tcc @@ -0,0 +1,37 @@ +#include + +struct adc_message* g_msg; + +EXO_TEST(test_message_refc_1, { + g_msg = adc_msg_create("IMSG Hello\\sWorld!"); + return g_msg != NULL; +}); + +EXO_TEST(test_message_refc_2, { + return g_msg->references == 0; // 0 +}); + +EXO_TEST(test_message_refc_3, { + adc_msg_incref(g_msg); + return g_msg->references == 1; // 1 +}); + +EXO_TEST(test_message_refc_4, { + adc_msg_incref(g_msg); + return g_msg->references == 2; // 2 +}); + +EXO_TEST(test_message_refc_5, { + adc_msg_free(g_msg); + return g_msg->references == 1; // 1 +}); + +EXO_TEST(test_message_refc_6, { + adc_msg_free(g_msg); + return g_msg->references == 0; // 0 +}); + +EXO_TEST(test_message_refc_7, { + adc_msg_free(g_msg); + return 1; +}); diff --git a/autotest/test_message.tcc b/autotest/test_message.tcc new file mode 100644 index 0000000..a91838e --- /dev/null +++ b/autotest/test_message.tcc @@ -0,0 +1,521 @@ +#include +static struct user* g_user = 0; +static const char* test_string1 = "IINF AAfoo BBbar CCwhat\n"; +static const char* test_string2 = "BMSG AAAB Hello\\sWorld!\n"; +static const char* test_string3 = "BINF AAAB IDAN7ZMSLIEBL53OPTM7WXGSTXUS3XOY6KQS5LBGX NIFriend DEstuff SL3 SS0 SF0 VEQuickDC/0.4.17 US6430 SUADC0,TCP4,UDP4 I4127.0.0.1 HO5 HN1 AW\n"; +static const char* test_string4 = "BMSG AAAB\n"; +static const char* test_string5 = "BMSG AAAB \n"; + +static void create_test_user() +{ + if (g_user) + return; + + g_user = (struct user*) malloc(sizeof(struct user)); + memset(g_user, 0, sizeof(struct user)); + memcpy(g_user->id.nick, "exotic-tester", 13); + g_user->id.sid = 1; +} + +EXO_TEST(adc_message_parse_1, { + struct adc_message* msg = adc_msg_create("IMSG Hello\\sWorld!"); + int ok = msg != NULL; + adc_msg_free(msg); + return ok; +}); + +EXO_TEST(adc_message_parse_2, { + struct adc_message* msg = adc_msg_create(test_string2); + return msg == NULL; +}); + +EXO_TEST(adc_message_parse_3, { + create_test_user(); + struct adc_message* msg = adc_msg_parse_verify(g_user, "BMSG AAAB Hello\\sWorld!", 23); + int ok = msg != NULL; + adc_msg_free(msg); + return ok; +}); + +EXO_TEST(adc_message_parse_4, { + struct adc_message* msg = adc_msg_parse_verify(g_user, "BMSG AAAC Hello\\sWorld!", 23); + return msg == NULL; +}); + +EXO_TEST(adc_message_parse_5, { + struct adc_message* msg = adc_msg_parse_verify(g_user, "BMSG AAAB Hello\\sWorld!\n", 24); + int ok = msg != NULL; + adc_msg_free(msg); + return ok; +}); + +EXO_TEST(adc_message_parse_6, { + struct adc_message* msg = adc_msg_parse_verify(g_user, "FMSG AAAB +TCP4 Hello\\sWorld!\n", 30); + int ok = msg != NULL; + adc_msg_free(msg); + return ok; +}); + +EXO_TEST(adc_message_parse_7, { + struct adc_message* msg = adc_msg_parse_verify(g_user, "FMSG AAAB -TCP4 Hello\\sWorld!\n", 30); + int ok = msg != NULL; + adc_msg_free(msg); + return ok; +}); + +EXO_TEST(adc_message_parse_8, { + struct adc_message* msg = adc_msg_parse_verify(g_user, "FMSG AAAB +TCP4+UDP4 Hello\\sWorld!\n", 35); + int ok = msg != NULL; + adc_msg_free(msg); + return ok; +}); + +EXO_TEST(adc_message_parse_9, { + struct adc_message* msg = adc_msg_parse_verify(g_user, "FMSG AAAB +TCP4-UDP4 Hello\\sWorld!\n", 35); + int ok = msg != NULL; + adc_msg_free(msg); + return ok; +}); + +EXO_TEST(adc_message_parse_10, { + struct adc_message* msg = adc_msg_parse_verify(g_user, "FMSG AAAB -TCP4-UDP4 Hello\\sWorld!\n", 35); + int ok = msg != NULL; + adc_msg_free(msg); + return ok; +}); + +EXO_TEST(adc_message_parse_11, { + struct adc_message* msg = adc_msg_parse_verify(g_user, "FMSG AAAB Hello\\sWorld!\n", 24); + return msg == NULL; +}); + +EXO_TEST(adc_message_parse_12, { + struct adc_message* msg = adc_msg_parse_verify(g_user, "FMSG AAAB Hello\\sWorld!\n", 25); + return msg == NULL; +}); + +EXO_TEST(adc_message_parse_13, { + struct adc_message* msg = adc_msg_parse_verify(g_user, "FMSG AAAB +jalla Hello\\sWorld!\n", 31); + return msg == NULL; +}); + +EXO_TEST(adc_message_parse_14, { + struct adc_message* msg = adc_msg_parse_verify(g_user, "FMSG AAAB +jall Hello\\sWorld!\n", 30); + int ok = msg != NULL; + adc_msg_free(msg); + return ok; +}); + +EXO_TEST(adc_message_parse_15, { + struct adc_message* msg = adc_msg_parse_verify(g_user, "FMSG AAAB +TCP4 Hello\\sWorld!\n", 30); + int ok = msg != NULL; + adc_msg_free(msg); + return ok; +}); + +EXO_TEST(adc_message_parse_16, { + struct adc_message* msg = adc_msg_parse_verify(g_user, "BMSG AAAB Hello\\sWorld!\n", 24); + int ok = msg != NULL; + adc_msg_free(msg); + return ok; +}); + +EXO_TEST(adc_message_parse_17, { + struct adc_message* msg = adc_msg_parse_verify(g_user, "BMSG aaab Hello\\sWorld!\n", 24); + return msg == NULL; +}); + +EXO_TEST(adc_message_parse_18, { + struct adc_message* msg = adc_msg_parse_verify(g_user, "DMSG AAAB AAAC Hello\\sthere!\n", 29); + int ok = msg != NULL; + adc_msg_free(msg); + return ok; +}); + +EXO_TEST(adc_message_parse_19, { + struct adc_message* msg = adc_msg_parse_verify(g_user, "DMSG AAAC AAAB Hello\\sthere!\n", 29); + return msg == NULL; +}); + +EXO_TEST(adc_message_parse_20, { + struct adc_message* msg = adc_msg_parse_verify(g_user, "EMSG AAAB AAAC Hello\\sthere!\n", 29); + int ok = msg != NULL; + adc_msg_free(msg); + return ok; +}); + + +EXO_TEST(adc_message_parse_21, { + struct adc_message* msg = adc_msg_parse_verify(g_user, "EMSG AAAC AAAB Hello\\sthere!\n", 29); + return msg == NULL; +}); + +EXO_TEST(adc_message_add_arg_1, { + struct adc_message* msg = adc_msg_create(test_string1); + adc_msg_add_argument(msg, "XXwtf?"); + int ok = strcmp(msg->cache, "IINF AAfoo BBbar CCwhat XXwtf?\n") == 0; + adc_msg_free(msg); + return ok; +}); + +EXO_TEST(adc_message_add_arg_2, { + struct adc_message* msg = adc_msg_create(test_string1); + adc_msg_add_named_argument(msg, "XX", "wtf?"); + int ok = strcmp(msg->cache, "IINF AAfoo BBbar CCwhat XXwtf?\n") == 0; + adc_msg_free(msg); + return ok; +}); + +EXO_TEST(adc_message_remove_arg_1, { + struct adc_message* msg = adc_msg_create(test_string1); + adc_msg_remove_named_argument(msg, "AA"); + int ok = strcmp(msg->cache, "IINF BBbar CCwhat\n") == 0; + adc_msg_free(msg); + return ok; +}); + +EXO_TEST(adc_message_remove_arg_2, { + struct adc_message* msg = adc_msg_create(test_string1); + adc_msg_remove_named_argument(msg, "BB"); + int ok = strcmp(msg->cache, "IINF AAfoo CCwhat\n") == 0; + adc_msg_free(msg); + return ok; +}); + +EXO_TEST(adc_message_remove_arg_3, { + struct adc_message* msg = adc_msg_create(test_string1); + adc_msg_remove_named_argument(msg, "CC"); + int ok = strcmp(msg->cache, "IINF AAfoo BBbar\n") == 0; + adc_msg_free(msg); + return ok; +}); + +EXO_TEST(adc_message_remove_arg_4, { + /* this ensures we can remove the last element also */ + struct adc_message* msg = adc_msg_parse_verify(g_user, test_string3, strlen(test_string3)); + adc_msg_remove_named_argument(msg, "AW"); + int ok = strcmp(msg->cache, "BINF AAAB IDAN7ZMSLIEBL53OPTM7WXGSTXUS3XOY6KQS5LBGX NIFriend DEstuff SL3 SS0 SF0 VEQuickDC/0.4.17 US6430 SUADC0,TCP4,UDP4 I4127.0.0.1 HO5 HN1\n") == 0; + adc_msg_free(msg); + return ok; +}); + +EXO_TEST(adc_message_replace_arg_1, { + struct adc_message* msg = adc_msg_create(test_string1); + adc_msg_remove_named_argument(msg, "AA"); + int ok = strcmp(msg->cache, "IINF BBbar CCwhat\n") == 0; + adc_msg_free(msg); + return ok; +}); + +EXO_TEST(adc_message_replace_arg_2, { + struct adc_message* msg = adc_msg_create(test_string1); + adc_msg_remove_named_argument(msg, "BB"); + int ok = strcmp(msg->cache, "IINF AAfoo CCwhat\n") == 0; + adc_msg_free(msg); + return ok; +}); + +EXO_TEST(adc_message_replace_arg_3, { + struct adc_message* msg = adc_msg_create(test_string1); + adc_msg_remove_named_argument(msg, "CC"); + int ok = strcmp(msg->cache, "IINF AAfoo BBbar\n") == 0; + adc_msg_free(msg); + return ok; +}); + +EXO_TEST(adc_message_get_arg_1, { + struct adc_message* msg = adc_msg_create(test_string1); + char* c = adc_msg_get_argument(msg, 0); + int ok = strcmp(c, "AAfoo") == 0; + adc_msg_free(msg); + hub_free(c); + return ok; +}); + +EXO_TEST(adc_message_get_arg_2, { + struct adc_message* msg = adc_msg_create(test_string1); + char* c = adc_msg_get_argument(msg, 1); + int ok = strcmp(c, "BBbar") == 0; + adc_msg_free(msg); + hub_free(c); + return ok; +}); + +EXO_TEST(adc_message_get_arg_3, { + struct adc_message* msg = adc_msg_create(test_string1); + char* c = adc_msg_get_argument(msg, 2); + int ok = strcmp(c, "CCwhat") == 0; + adc_msg_free(msg); + hub_free(c); + return ok; +}); + +EXO_TEST(adc_message_get_arg_4, { + struct adc_message* msg = adc_msg_create(test_string1); + char* c = adc_msg_get_argument(msg, 3); + int ok = c == 0; + adc_msg_free(msg); + hub_free(c); + return ok; +}); + +EXO_TEST(adc_message_get_named_arg_1, { + struct adc_message* msg = adc_msg_create(test_string1); + char* c = adc_msg_get_named_argument(msg, "AA"); + int ok = strcmp(c, "foo") == 0; + adc_msg_free(msg); + hub_free(c); + return ok; +}); + +EXO_TEST(adc_message_get_named_arg_2, { + struct adc_message* msg = adc_msg_create(test_string1); + char* c = adc_msg_get_named_argument(msg, "BB"); + int ok = strcmp(c, "bar") == 0; + adc_msg_free(msg); + hub_free(c); + return ok; +}); + +EXO_TEST(adc_message_get_named_arg_3, { + struct adc_message* msg = adc_msg_create(test_string1); + char* c = adc_msg_get_named_argument(msg, "CC"); + int ok = strcmp(c, "what") == 0; + adc_msg_free(msg); + hub_free(c); + return ok; +}); + +EXO_TEST(adc_message_get_named_arg_4, { + struct adc_message* msg = adc_msg_create(test_string1); + char* c = adc_msg_get_named_argument(msg, "XX"); + int ok = c == 0; + adc_msg_free(msg); + hub_free(c); + return ok; +}); + +EXO_TEST(adc_message_has_named_arg_1, { + struct adc_message* msg = adc_msg_create(test_string1); + int n = adc_msg_has_named_argument(msg, "XX"); + adc_msg_free(msg); + return n == 0; +}); + +EXO_TEST(adc_message_has_named_arg_2, { + struct adc_message* msg = adc_msg_create(test_string1); + int n = adc_msg_has_named_argument(msg, "BB"); + adc_msg_free(msg); + return n == 1; +}); + +EXO_TEST(adc_message_has_named_arg_3, { + struct adc_message* msg = adc_msg_create(test_string1); + int n = adc_msg_has_named_argument(msg, "CC"); + adc_msg_free(msg); + return n == 1; +}); + +EXO_TEST(adc_message_has_named_arg_4, { + struct adc_message* msg = adc_msg_create(test_string1); + adc_msg_add_argument(msg, "XXwtf?"); + int n = adc_msg_has_named_argument(msg, "XX"); + adc_msg_free(msg); + return n == 1; +}); + +EXO_TEST(adc_message_has_named_arg_5, { + struct adc_message* msg = adc_msg_create(test_string1); + adc_msg_add_argument(msg, "XXone"); + adc_msg_add_argument(msg, "XXtwo"); + int n = adc_msg_has_named_argument(msg, "XX"); + adc_msg_free(msg); + return n == 2; +}); + +EXO_TEST(adc_message_has_named_arg_6, { + struct adc_message* msg = adc_msg_create(test_string1); + adc_msg_add_argument(msg, "XXone"); + adc_msg_add_argument(msg, "XXtwo"); + adc_msg_add_argument(msg, "XXthree"); + int n = adc_msg_has_named_argument(msg, "XX"); + adc_msg_free(msg); + return n == 3; +}); + +EXO_TEST(adc_message_has_named_arg_7, { + struct adc_message* msg = adc_msg_create(test_string1); + int n = adc_msg_has_named_argument(msg, "AA"); + adc_msg_free(msg); + return n == 1; +}); + +EXO_TEST(adc_message_terminate_1, { + struct adc_message* msg = adc_msg_create("IINF AAfoo BBbar CCwhat"); + adc_msg_unterminate(msg); + int ok = strcmp(msg->cache, "IINF AAfoo BBbar CCwhat") == 0; + adc_msg_free(msg); + return ok; +}); + +EXO_TEST(adc_message_terminate_2, { + struct adc_message* msg = adc_msg_create(test_string1); + adc_msg_unterminate(msg); + adc_msg_terminate(msg); + int ok = strcmp(msg->cache, "IINF AAfoo BBbar CCwhat\n") == 0; + adc_msg_free(msg); + return ok; +}); + +EXO_TEST(adc_message_terminate_3, { + struct adc_message* msg = adc_msg_create(test_string1); + adc_msg_unterminate(msg); + adc_msg_terminate(msg); + adc_msg_unterminate(msg); + int ok = strcmp(msg->cache, "IINF AAfoo BBbar CCwhat") == 0; + adc_msg_free(msg); + return ok; +}); + +EXO_TEST(adc_message_terminate_4, { + struct adc_message* msg = adc_msg_create(test_string1); + adc_msg_unterminate(msg); + adc_msg_terminate(msg); + adc_msg_terminate(msg); + int ok = strcmp(msg->cache, "IINF AAfoo BBbar CCwhat\n") == 0; + adc_msg_free(msg); + return ok; +}); + +EXO_TEST(adc_message_terminate_5, { + struct adc_message* msg = adc_msg_create(test_string1); + adc_msg_terminate(msg); + adc_msg_terminate(msg); + int ok = strcmp(msg->cache, "IINF AAfoo BBbar CCwhat\n") == 0; + adc_msg_free(msg); + return ok; +}); + +EXO_TEST(adc_message_terminate_6, { + struct adc_message* msg = adc_msg_create(test_string1); + adc_msg_unterminate(msg); + adc_msg_unterminate(msg); + int ok = strcmp(msg->cache, "IINF AAfoo BBbar CCwhat") == 0; + adc_msg_free(msg); + return ok; +}); + +EXO_TEST(adc_message_escape_1, { + char* s = adc_msg_escape(test_string1); + int ok = strcmp(s, "IINF\\sAAfoo\\sBBbar\\sCCwhat\\n") == 0; + hub_free(s); + return ok; +}); + +EXO_TEST(adc_message_escape_2, { + char* s = adc_msg_escape(test_string1); + char* s2 = adc_msg_unescape(s); + int ok = strcmp(s2, test_string1) == 0; + hub_free(s); + hub_free(s2); + return ok; +}); + +EXO_TEST(adc_message_escape_3, { + char* s = adc_msg_unescape(test_string1); + int ok = strcmp(s, test_string1) == 0; + hub_free(s); + return ok; +}); + +EXO_TEST(adc_message_copy_1, { + struct adc_message* msg1 = adc_msg_create(test_string1); + struct adc_message* msg2 = adc_msg_copy(msg1); + int ok = strncmp(msg1->cache, msg2->cache, msg1->length) == 0; + adc_msg_free(msg1); + adc_msg_free(msg2); + return ok; +}); + +EXO_TEST(adc_message_copy_2, { + struct adc_message* msg1 = adc_msg_parse_verify(g_user, test_string2, strlen(test_string2)); + struct adc_message* msg2 = adc_msg_copy(msg1); + int ok = msg1->source == msg2->source; + adc_msg_free(msg1); + adc_msg_free(msg2); + return ok; +}); + +EXO_TEST(adc_message_copy_3, { + struct adc_message* msg1 = adc_msg_parse_verify(g_user, test_string2, strlen(test_string2)); + struct adc_message* msg2 = adc_msg_copy(msg1); + int ok = ( msg1->cmd == msg2->cmd && + msg1->source == msg2->source && + msg1->target == msg2->target && + msg1->length == msg2->length && + msg1->priority == msg2->priority && + msg1->capacity == msg2->capacity && /* might not be true! */ + strcmp(msg1->cache, msg2->cache) == 0); + adc_msg_free(msg1); + adc_msg_free(msg2); + return ok; +}); + +EXO_TEST(adc_message_copy_4, { + struct adc_message* msg1 = adc_msg_parse_verify(g_user, test_string2, strlen(test_string2)); + struct adc_message* msg2 = adc_msg_copy(msg1); + int ok = msg2->target == 0; + adc_msg_free(msg1); + adc_msg_free(msg2); + return ok; +}); + + +static struct adc_message* updater1 = NULL; +static struct adc_message* updater2 = NULL; +static const char* update_info1 = "BINF AAAB IDABCDEFGHIJKLMNOPQRSTUVWXYZ1234567ABCDEF NItester SL10 SS12817126127 SF4125 HN3 HR0 HO0 VE++\\s0.698 US104857600 DS81911808 SUTCP4,UDP4 I4127.0.0.1\n"; +static const char* update_info2 = "BINF AAAB HN34 SF4126 SS12817526127\n"; + +EXO_TEST(adc_message_update_1, { + updater1 = adc_msg_parse_verify(g_user, update_info1, strlen(update_info1)); + return updater1 != NULL; +}); + +EXO_TEST(adc_message_update_2, { + user_set_info(g_user, updater1); + return strcmp(g_user->info->cache, updater1->cache) == 0 && g_user->info == updater1; +}); + +EXO_TEST(adc_message_update_3, { + updater2 = adc_msg_parse_verify(g_user, update_info2, strlen(update_info2)); + return updater2 != NULL; +}); + +extern void update_user_info(struct user* u, struct adc_message* cmd); + +EXO_TEST(adc_message_update_4, { + update_user_info(g_user, updater2); + return strlen(g_user->info->cache) == 159; +}); + +EXO_TEST(adc_message_empty_1, { + struct adc_message* msg = adc_msg_parse_verify(g_user, test_string2, strlen(test_string2)); + int ok = adc_msg_is_empty(msg) == 0; + adc_msg_free(msg); + return ok; +}); + +EXO_TEST(adc_message_empty_2, { + struct adc_message* msg = adc_msg_parse_verify(g_user, test_string4, strlen(test_string4)); + int ok = adc_msg_is_empty(msg); + adc_msg_free(msg); + return ok; +}); + +EXO_TEST(adc_message_empty_3, { + struct adc_message* msg = adc_msg_parse_verify(g_user, test_string5, strlen(test_string5)); + int ok = adc_msg_is_empty(msg) == 0; /* arguably not empty, contains a space */ + adc_msg_free(msg); + return ok; +}); + diff --git a/autotest/test_misc.tcc b/autotest/test_misc.tcc new file mode 100644 index 0000000..5b72f03 --- /dev/null +++ b/autotest/test_misc.tcc @@ -0,0 +1,103 @@ +#include + +EXO_TEST(is_num_0, { return is_num('0'); }); +EXO_TEST(is_num_1, { return is_num('1'); }); +EXO_TEST(is_num_2, { return is_num('2'); }); +EXO_TEST(is_num_3, { return is_num('3'); }); +EXO_TEST(is_num_4, { return is_num('4'); }); +EXO_TEST(is_num_5, { return is_num('5'); }); +EXO_TEST(is_num_6, { return is_num('6'); }); +EXO_TEST(is_num_7, { return is_num('7'); }); +EXO_TEST(is_num_8, { return is_num('8'); }); +EXO_TEST(is_num_9, { return is_num('9'); }); +EXO_TEST(is_num_10, { return !is_num('/'); }); +EXO_TEST(is_num_11, { return !is_num(':'); }); + +EXO_TEST(is_space_1, { return is_space(' '); }); +EXO_TEST(is_space_2, { return !is_space('\t'); }); +EXO_TEST(is_white_space_1, { return is_white_space(' '); }); +EXO_TEST(is_white_space_2, { return is_white_space('\t'); }); +EXO_TEST(is_white_space_3, { return !is_white_space('A'); }); +EXO_TEST(is_white_space_4, { return !is_white_space('!'); }); + +EXO_TEST(itoa_1, { return strcmp(uhub_itoa(0), "0") == 0; }); +EXO_TEST(itoa_2, { return strcmp(uhub_itoa(1), "1") == 0; }); +EXO_TEST(itoa_3, { return strcmp(uhub_itoa(-1), "-1") == 0; }); +EXO_TEST(itoa_4, { return strcmp(uhub_itoa(255), "255") == 0; }); +EXO_TEST(itoa_5, { return strcmp(uhub_itoa(-3), "-3") == 0; }); +EXO_TEST(itoa_6, { return strcmp(uhub_itoa(-2147483647), "-2147483647") == 0; }); +EXO_TEST(itoa_7, { return strcmp(uhub_itoa(2147483647), "2147483647") == 0; }); +EXO_TEST(itoa_8, { return strcmp(uhub_itoa(-65536), "-65536") == 0; }); + +EXO_TEST(base32_valid_1, { return is_valid_base32_char('A'); }); +EXO_TEST(base32_valid_2, { return is_valid_base32_char('B'); }); +EXO_TEST(base32_valid_3, { return is_valid_base32_char('C'); }); +EXO_TEST(base32_valid_4, { return is_valid_base32_char('D'); }); +EXO_TEST(base32_valid_5, { return is_valid_base32_char('E'); }); +EXO_TEST(base32_valid_6, { return is_valid_base32_char('F'); }); +EXO_TEST(base32_valid_7, { return is_valid_base32_char('G'); }); +EXO_TEST(base32_valid_8, { return is_valid_base32_char('H'); }); +EXO_TEST(base32_valid_9, { return is_valid_base32_char('I'); }); +EXO_TEST(base32_valid_10, { return is_valid_base32_char('J'); }); +EXO_TEST(base32_valid_11, { return is_valid_base32_char('K'); }); +EXO_TEST(base32_valid_12, { return is_valid_base32_char('L'); }); +EXO_TEST(base32_valid_13, { return is_valid_base32_char('M'); }); +EXO_TEST(base32_valid_14, { return is_valid_base32_char('N'); }); +EXO_TEST(base32_valid_15, { return is_valid_base32_char('O'); }); +EXO_TEST(base32_valid_16, { return is_valid_base32_char('P'); }); +EXO_TEST(base32_valid_17, { return is_valid_base32_char('Q'); }); +EXO_TEST(base32_valid_18, { return is_valid_base32_char('R'); }); +EXO_TEST(base32_valid_19, { return is_valid_base32_char('S'); }); +EXO_TEST(base32_valid_20, { return is_valid_base32_char('T'); }); +EXO_TEST(base32_valid_21, { return is_valid_base32_char('U'); }); +EXO_TEST(base32_valid_22, { return is_valid_base32_char('V'); }); +EXO_TEST(base32_valid_23, { return is_valid_base32_char('W'); }); +EXO_TEST(base32_valid_24, { return is_valid_base32_char('X'); }); +EXO_TEST(base32_valid_25, { return is_valid_base32_char('Y'); }); +EXO_TEST(base32_valid_26, { return is_valid_base32_char('Z'); }); +EXO_TEST(base32_valid_27, { return is_valid_base32_char('2'); }); +EXO_TEST(base32_valid_28, { return is_valid_base32_char('3'); }); +EXO_TEST(base32_valid_29, { return is_valid_base32_char('4'); }); +EXO_TEST(base32_valid_30, { return is_valid_base32_char('5'); }); +EXO_TEST(base32_valid_31, { return is_valid_base32_char('6'); }); +EXO_TEST(base32_valid_32, { return is_valid_base32_char('7'); }); + +EXO_TEST(base32_invalid_1, { return !is_valid_base32_char('a'); }); +EXO_TEST(base32_invalid_2, { return !is_valid_base32_char('b'); }); +EXO_TEST(base32_invalid_3, { return !is_valid_base32_char('c'); }); +EXO_TEST(base32_invalid_4, { return !is_valid_base32_char('d'); }); +EXO_TEST(base32_invalid_5, { return !is_valid_base32_char('e'); }); +EXO_TEST(base32_invalid_6, { return !is_valid_base32_char('f'); }); +EXO_TEST(base32_invalid_7, { return !is_valid_base32_char('g'); }); +EXO_TEST(base32_invalid_8, { return !is_valid_base32_char('h'); }); +EXO_TEST(base32_invalid_9, { return !is_valid_base32_char('i'); }); +EXO_TEST(base32_invalid_10, { return !is_valid_base32_char('j'); }); +EXO_TEST(base32_invalid_11, { return !is_valid_base32_char('k'); }); +EXO_TEST(base32_invalid_12, { return !is_valid_base32_char('l'); }); +EXO_TEST(base32_invalid_13, { return !is_valid_base32_char('m'); }); +EXO_TEST(base32_invalid_14, { return !is_valid_base32_char('n'); }); +EXO_TEST(base32_invalid_15, { return !is_valid_base32_char('o'); }); +EXO_TEST(base32_invalid_16, { return !is_valid_base32_char('p'); }); +EXO_TEST(base32_invalid_17, { return !is_valid_base32_char('q'); }); +EXO_TEST(base32_invalid_18, { return !is_valid_base32_char('r'); }); +EXO_TEST(base32_invalid_19, { return !is_valid_base32_char('s'); }); +EXO_TEST(base32_invalid_20, { return !is_valid_base32_char('t'); }); +EXO_TEST(base32_invalid_21, { return !is_valid_base32_char('u'); }); +EXO_TEST(base32_invalid_22, { return !is_valid_base32_char('v'); }); +EXO_TEST(base32_invalid_23, { return !is_valid_base32_char('w'); }); +EXO_TEST(base32_invalid_24, { return !is_valid_base32_char('x'); }); +EXO_TEST(base32_invalid_25, { return !is_valid_base32_char('y'); }); +EXO_TEST(base32_invalid_26, { return !is_valid_base32_char('z'); }); +EXO_TEST(base32_invalid_27, { return !is_valid_base32_char('0'); }); +EXO_TEST(base32_invalid_28, { return !is_valid_base32_char('1'); }); +EXO_TEST(base32_invalid_29, { return !is_valid_base32_char('8'); }); +EXO_TEST(base32_invalid_30, { return !is_valid_base32_char('9'); }); +EXO_TEST(base32_invalid_31, { return !is_valid_base32_char('@'); }); + +EXO_TEST(utf8_valid_1, { return is_valid_utf8("abcdefghijklmnopqrstuvwxyz"); }); +EXO_TEST(utf8_valid_2, { return is_valid_utf8("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); }); +EXO_TEST(utf8_valid_3, { return is_valid_utf8("0123456789"); }); +EXO_TEST(utf8_valid_4, { return is_valid_utf8( (char[]) { 0x65, 0x00} ); }); +EXO_TEST(utf8_valid_5, { return !is_valid_utf8( (char[]) { 0xD8, 0x00} ); }); + + diff --git a/autotest/test_tiger.tcc b/autotest/test_tiger.tcc new file mode 100644 index 0000000..e481fea --- /dev/null +++ b/autotest/test_tiger.tcc @@ -0,0 +1,69 @@ +#include + +#define DEBUG_HASH + +static char* byte_to_hex(char* dest, uint8_t c) +{ + static const char* hexchars = "0123456789abcdef"; + *dest = hexchars[c / 16]; dest++; + *dest = hexchars[c % 16]; dest++; + return dest; +} + + +static int test_tiger_hex(char* input, char* expected) { + char buf[TIGERSIZE*2+1]; + uint64_t tiger_res[3]; + int i = 0; +#ifdef DEBUG_HASH + int res = 0; +#endif + char* ptr = buf; + buf[TIGERSIZE*2] = 0; + + tiger((uint64_t*) input, strlen(input), (uint64_t*) tiger_res); + for (i = 0; i < TIGERSIZE; i++) + ptr = byte_to_hex(ptr, (char) (((uint8_t*) tiger_res)[i]) ); + +#ifdef DEBUG_HASH + res = strcasecmp(buf, expected) == 0 ? 1 : 0; + + if (!res) + { + printf("Expected: '%s', Got: '%s'\n", expected, buf); + } + return res; +#else + return strcasecmp(buf, expected) == 0; +#endif +} + + +EXO_TEST(hash_tiger_1, { + return test_tiger_hex("", "3293AC630C13F0245F92BBB1766E16167A4E58492DDE73F3"); +}); + +EXO_TEST(hash_tiger_2, { + return test_tiger_hex("a", "77BEFBEF2E7EF8AB2EC8F93BF587A7FC613E247F5F247809"); +}); + +EXO_TEST(hash_tiger_3, { + return test_tiger_hex("abc", "2AAB1484E8C158F2BFB8C5FF41B57A525129131C957B5F93"); +}); + +EXO_TEST(hash_tiger_4, { + return test_tiger_hex("message digest", "D981F8CB78201A950DCF3048751E441C517FCA1AA55A29F6"); +}); + +EXO_TEST(hash_tiger_5, { + return test_tiger_hex("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", "0F7BF9A19B9C58F2B7610DF7E84F0AC3A71C631E7B53F78E"); +}); + +EXO_TEST(hash_tiger_6, { + return test_tiger_hex("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", "8DCEA680A17583EE502BA38A3C368651890FFBCCDC49A8CC"); +}); + +EXO_TEST(hash_tiger_7, { + return test_tiger_hex("12345678901234567890123456789012345678901234567890123456789012345678901234567890", "1C14795529FD9F207A958F84C52F11E887FA0CABDFD91BFD"); +}); + diff --git a/doc/Doxyfile b/doc/Doxyfile new file mode 100644 index 0000000..d22521e --- /dev/null +++ b/doc/Doxyfile @@ -0,0 +1,256 @@ +# Doxyfile 1.5.5-KDevelop + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = uHub +PROJECT_NUMBER = 0.2.0-alpha +OUTPUT_DIRECTORY = +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the +ALWAYS_DETAILED_SEC = YES +INLINE_INHERITED_MEMB = YES +FULL_PATH_NAMES = NO +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = YES +JAVADOC_AUTOBRIEF = NO +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +DETAILS_AT_TOP = YES +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 8 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = YES +OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +BUILTIN_STL_SUPPORT = NO +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = YES +DISTRIBUTE_GROUP_DOC = NO +SUBGROUPING = YES +TYPEDEF_HIDES_STRUCT = NO +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = YES +EXTRACT_PRIVATE = YES +EXTRACT_STATIC = YES +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = YES +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_GROUP_NAMES = NO +SORT_BY_SCOPE_NAME = YES +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_DIRECTORIES = NO +SHOW_FILES = YES +SHOW_NAMESPACES = YES +FILE_VERSION_FILTER = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = YES +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = . +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = *.c \ + *.h +RECURSIVE = YES +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = autotest/* +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = YES +INLINE_SOURCES = YES +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = YES +REFERENCES_RELATION = YES +REFERENCES_LINK_SOURCE = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = YES +GENERATE_DOCSET = NO +DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_BUNDLE_ID = org.doxygen.Project +HTML_DYNAMIC_SECTIONS = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +BINARY_TOC = NO +TOC_EXPAND = YES +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NONE +TREEVIEW_WIDTH = 250 +FORMULA_FONTSIZE = 10 +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = uhub.tag +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +MSCGEN_PATH = +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = YES +DOT_FONTNAME = FreeSans +DOT_FONTPATH = +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = NO +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 1000 +DOT_TRANSPARENT = YES +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = NO diff --git a/doc/architecture.txt b/doc/architecture.txt new file mode 100644 index 0000000..8007d3f --- /dev/null +++ b/doc/architecture.txt @@ -0,0 +1,91 @@ += Architecture of uHub = + +uHub is single threaded and handles network and timer events using the +libevent library. +For each state there is a read event (and sometimes a write event) and timeout +event in case an expected read (or write) event does not occur. + + +== Protocol overview == +uHub use "speak" the ADC protocol, which works in short as follows: +(C = client, S = server aka uHub). + +C: HSUP ADBASE +Client connects to hub and supplies a list of supported features to the hub. + +S: ISUP ADBASE { } +Server responds with a list of supported features. + +S: ISID xxxx +Server assigns a session-ID to the client (4 bytes, BASE32 encoded). + +C: BINF xxxx (...) +Client sends information about itself, such as nick name, etc. +The hub will relay this information to all users, including the client. + +S: BINF xxx1 NInick1 (...) +S: BINF xxx2 NInick2 +S: (...) +S: BINF xxxx (client's own nick) +Client gets list of other clients, which ends with the client's own user info. +At this point the client is successfully logged into the hub. + + +== The hub architecture == + +Accepting new users + + + + + + --------------------- ----------------- + | Accept connection | <---------- | libevent loop | + --------------------- ----------------- + | + V + --------------------- ------------ + | Setup login timer | --+----> | Timeout? | <----+ + --------------------- | ------------ | + | | | | + V | V | + --------------------- | --------------- | + | Receive 'HSUP' | --+----> | DISCONNECT! | | + --------------------- --------------- | + | ^ | + V | | + --------------------- | | + | Send 'ISUP', and | | | + | assign Session ID | | | + --------------------- | | + | | | + V | | + --------------------- | | + | Receive 'BINF' |-----------------+------------ + +--| Validate message | ^ + | --------------------- | + | | | + | V | + | --------------------- --------------------- + | | Send password | ------> | Reveive and check | + | | request, if needed| | password. | + | --------------------- --------------------- + | | + | | + | ------------------------ | + +->| Send welcome message |<--------------+ + ------------------------ + | + V + ------------------------ + | Send user list to | + | newly accepted user. | + ------------------------ + | + V + ------------------------ + | User is logged in. | + | Announce to all. | + ------------------------ + + diff --git a/doc/extensions.txt b/doc/extensions.txt new file mode 100644 index 0000000..7cfc0b3 --- /dev/null +++ b/doc/extensions.txt @@ -0,0 +1,76 @@ +ADC protocol extensions supported: + +STATUS: **** Not yet implemented. + +the 'AUT0' extension (network connection auto detection extension). +Rationale: Detect if client is capable of initiating active connections. + +After logging in: + +Client -> 'HCHK 12345 ABCD'. + +Server sends a UDP packet containing token to the client's IP address at the given port as shown in the INF message. + +Server -> 'ICHK ABCD' + +The server should send it from a UDP socket bound to the same port as the TCP server, which means the server will +have to listen to both TCP and UDP. + +If client receives the packet, it knows it can receive UDP connections, and will advertise it in the INF message as +a supported feature. + +If the client does not receive any UDP packets within a few seconds, it MAY try to reconfigure the router using +UPnP/ZeroConf, and issue a HCHK command to the server again. + +If the client still doesn't receive any UDP packets, hole punching can be tried. +The server will send a UDP packet to the hub (using the port of the TCP server), and reissue the HCHK command. +The UDP packet SHOULD be echoed by the hub. +This UDP packet should contain simply 'HECH {cid} {token}' (Hub echo). + +The hub should send a packet containing the token back: +'IECH {token} {host:port}', aswell as the same message via TCP. + +If the client receives the message via UDP, it should now be able to determine the type of NAT. +If the client receives the message via TCP only it knows it has a firewall blocking icomming communication. +If the client does not receive the message, it should assume a firewall is blocking all UDP communication, +and resume in passive mode. + +Requirements: +'AUT0' in the extensions message (SUP) for client and hub. +Server will also listen to UDP messages on the same port as the TCP port. + +The server MUST now respond to the 'HCHK' command via TCP +The server MUST now respond to the 'HECH' command via UDP. + +The client will always initiate communication. + +------------------------------------------------------------------------------- + +Syntax: HCHK {port} {token} +- port is a 16-bit port number where the client is listening for packets. +- token is 4 bytes base32 encoded data specified by the client. + +Example: +Client: 'HCHK 1511 BACD' (tcp) +Server: 'ICHK BACD' (udp) + +------------------------------------------------------------------------------- + +Syntax: HECH {cid} {token} +- cid is the client ID. +- token is 4 bytes base32 encoded data specified by the client. + +Example: +Client: 'HECH 3NGFVJUDBRHX2CYRJGQ5HACRDM5CTNLGZ36M64I BACD' (udp) +Server: 'IECH BACD 192.168.0.1:1512' (udp) +Server: 'IECH BACD 192.168.0.1:1512' (tcp) + +Security considerations: +The hub must verify that IP address where the HECH command originated from +matches the IP address where the user with the given CID is connected from. +If the CID and IP does not match, or the CID is not used the hub MUST ignore +the request. + + + + diff --git a/doc/uhub.conf b/doc/uhub.conf new file mode 100644 index 0000000..4539108 --- /dev/null +++ b/doc/uhub.conf @@ -0,0 +1,66 @@ +# uhub.conf - A example configuration file. +# You should normally place this file in /etc/uhub/uhub.conf +# And change the file_acl and file_motd below. +# +# This file is read only to the uhub deamon, and if you +# make changes to it while uhub is running you can send a +# HUP signal to it, to reparse configuration (only on UNIX). + +# Bind to this port and address +# server_bind_addr=any means listen to "::" if IPv6 is supported +# by the host OS, otherwise 0.0.0.0. +server_port=1511 +server_bind_addr=any + +# The maximum amount of users allowed on the hub. +max_users=500 + +# If 1, will show a "This hub is running uhub/version". +show_banner=1 + +# Allow only registered users on the hub if set to 1. +registered_users_only=0 + +# A server name and description. +hub_name=my hub +hub_description=Powered by uHub + +# Set this to 0, and the hub will disconnect everyone +hub_enabled=1 + +# Access control list (user database) +file_acl=/etc/uhub/users.conf + +# This file can contain a message of the day. A welcome +# message send to any client when connecting. +# If the file does not exist, is empty, or cannot be opened +# the motd will not be sent to the clients. +# Normally this message is sent to clients when connecting. +file_motd=/etc/uhub/motd.txt + +# Configure status message as sent to clients in different circumstances. +msg_hub_full = Hub is full +msg_hub_disabled = Hub is disabled +msg_hub_registered_users_only = Hub is for registered users only +msg_inf_error_nick_missing = No nickname given +msg_inf_error_nick_multiple = Multiple nicknames given +msg_inf_error_nick_invalid = Nickname is invalid +msg_inf_error_nick_long = Nickname too long +msg_inf_error_nick_short = Nickname too short +msg_inf_error_nick_spaces = Nickname cannot start with spaces +msg_inf_error_nick_bad_chars = Nickname contains invalid characters +msg_inf_error_nick_not_utf8 = Nickname is not valid utf8 +msg_inf_error_nick_taken = Nickname is already in use +msg_inf_error_nick_restricted = Nickname cannot be used on this hub +msg_inf_error_cid_invalid = CID is not valid +msg_inf_error_cid_missing = CID is not specified +msg_inf_error_cid_taken = CID is taken +msg_inf_error_pid_missing = PID is not specified +msg_inf_error_pid_invalid = PID is invalid +msg_ban_permanently = Banned permanently +msg_ban_temporarily = Banned temporarily +msg_auth_invalid_password = Password is wrong +msg_auth_user_not_found = User not found in password database +msg_error_no_memory = No memory + + diff --git a/doc/uhub.dot b/doc/uhub.dot new file mode 100644 index 0000000..50dec07 --- /dev/null +++ b/doc/uhub.dot @@ -0,0 +1,59 @@ +/** + * Overview of uHub + */ +digraph G +{ + + "main()" -> "event_dispatch()" + + "event_dispatch()" -> "net_on_accept()" + "event_dispatch()" -> "net_on_read()" + "event_dispatch()" -> "net_on_write()" + "event_dispatch()" -> "event_queue_process()" + + /* net events */ + "net_on_write()" -> "net_send()" + "net_on_write()" -> "user_disconnect()" + "net_on_accept()" -> "user_create()" + "net_on_read()" -> "net_recv()" + "net_on_read()" -> "user_disconnect()" + + "net_recv()" -> "hub_handle_message()" + + /* adc message handling */ + "hub_handle_message()" -> "hub_handle_password()" + "hub_handle_message()" -> "hub_handle_support()" + + "hub_handle_password()" -> "user_disconnect()" + "hub_handle_support()" -> "user_disconnect()" + "hub_handle_password()" -> "route_to_user()" + "hub_handle_support()" -> "route_to_user()" + + "hub_handle_message()" -> "hub_handle_info()" + "hub_handle_message()" -> "route_message()" + "hub_handle_info()" -> "route_to_user()" + "hub_handle_info()" -> "route_to_all()" + + /* message routing, depending on message type */ + "route_message()" -> "route_to_user()" + "route_message()" -> "route_to_all()" -> "route_to_user()" + "route_message()" -> "route_to_subscribers()" -> "route_to_user()" + + "route_to_user()" -> "net_send()" + "route_to_user()" -> "queue_command()" + "route_to_user()" -> "user_disconnect()" + + + /* Message dispatcher */ + "event_queue_process()" -> "hub_event_dispatcher()" + "hub_event_dispatcher()" -> "EVENT_USER_JOIN" + "hub_event_dispatcher()" -> "EVENT_USER_QUIT" + "EVENT_USER_QUIT" -> "route_to_all()" + "EVENT_USER_QUIT" -> "user_destroy()" + "EVENT_USER_JOIN" -> "route_to_all()" + + /* user_disconnect() -- critical part */ + "user_disconnect()" -> "user_set_state(state_cleanup)" -> "post: EVENT_USER_QUIT" + "user_disconnect()" -> "user_destroy()" + +} diff --git a/doc/users.conf b/doc/users.conf new file mode 100644 index 0000000..9a636eb --- /dev/null +++ b/doc/users.conf @@ -0,0 +1,51 @@ +# uHub access control lists. +# +# Syntax: [data] +# +# commands: +# 'user_reg' - registered user with no particular privileges (data=nick:password) +# 'user_op' - operator, can kick or ban people (data=nick:password) +# 'user_admin' - administrator, can do everything operators can, and reconfigure the hub (data=nick:password) +# 'deny_nick' - nick name that is not accepted (example; Administrator) +# 'deny_ip' - Unacceptable IP (masks can be specified as CIDR: 0.0.0.0/32 will block all IPv4) +# 'ban_nick' - banned user by nick +# 'ban_cid' - banned user by cid + +# Administrator +user_admin Dj_Offset:uhub +user_op janvidar:password + +# We don't want users with these names +deny_nick Hub-Security +deny_nick Administrator +deny_nick root +deny_nick admin +deny_nick username +deny_nick user +deny_nick guest +deny_nick operator + +# Banned users +# ban_nick H4X0R +# ban_cid FOIL5EK2UDZYAXT7UIUFEKL4SEBEAJE3INJDKAY + +# ban by ip +# +# to ban by CIDR +# deny_ip 10.21.44.0/24 +# +# to ban by IP-range. +# deny_ip 10.21.44.7-10.21.44.9 +# +# to ban a single IP address +# deny_ip 10.21.44.7 +# (which is equivalent to using): +# deny_ip 10.21.44.7/32 + +# Will not work, yet +# nat_ip 10.0.0.0/8 +# nat_ip 127.0.0.0/8 + +# If you have made changes to this file, you must send a HANGUP signal +# to uHub so that it will re-read the configuration files. +# For example by invoking: 'killall -HUP uhub' diff --git a/release_setup.mk b/release_setup.mk new file mode 100644 index 0000000..3212cb3 --- /dev/null +++ b/release_setup.mk @@ -0,0 +1,29 @@ +ifeq ($(RELEASE),YES) +HOST_SYSTEM ?= $(shell uname -s | tr [:upper:] [:lower:] | sed s/darwin/macosx/ ) + +ifeq ($(HOST_SYSTEM), macosx) +HOST_MACHINE ?= $(shell uname -p | tr [:upper:] [:lower:] ) +else +HOST_MACHINE ?= $(shell uname -m | tr [:upper:] [:lower:] | sed s/i686/i386/ | sed s/x86_64/amd64/ | sed s/ppc64/powerpc/ ) +endif + +ifeq ($(HOST_SYSTEM), mingw32_nt-5.1) +HOST_SYSTEM = win32 +endif + +VERSION ?= $(shell grep define\ VERSION version.h | cut -f 3 -d " " | tr -d [=\"=] ) +SNAPSHOT ?= $(shell date '+%Y%m%d' ) + +REVISION ?= $(shell svn info | grep Revision | tr -d [:alpha:][:punct:][:space:] ) +PACKAGE ?= uhub-$(VERSION)-$(REVISION) +PACKAGE_SRC ?= $(PACKAGE)-src +PACKAGE_BIN ?= $(PACKAGE)-$(HOST_SYSTEM)-$(HOST_MACHINE) + +URL_ARCHIVE='build-archive:~/uhub/' +URL_PUBLISH='domeneshop:~/www/downloads/uhub/' +URL_SNAPSHOT='domeneshop:~/www/downloads/uhub/snapshots/' + +else +autotest_TMP = autotest.c +endif + diff --git a/release_targets.mk b/release_targets.mk new file mode 100644 index 0000000..d1535c2 --- /dev/null +++ b/release_targets.mk @@ -0,0 +1,80 @@ +autotest.c: $(autotest_SOURCES) + $(shell exotic --standalone $(autotest_SOURCES) > $@) + +ifeq ($(RELEASE),YES) + +%.tar.bz2: %.tar + @bzip2 -c -9 $^ > $@ + +%.tar.gz: %.tar + @gzip -c -9 $^ > $@ + +ChangeLog-$(VERSION)-$(REVISION): ChangeLog + @cp $^ $@ + +changelog: ChangeLog-$(VERSION)-$(REVISION) + +$(PACKAGE_SRC).tar $(PACKAGE_SRC).zip: autotest.c + @if [ -d $(PACKAGE) ]; then rm -Rf $(PACKAGE); fi + @svn export . $(PACKAGE) > /dev/null + @rm -f $(PACKAGE)/release_*.mk + @grep -v \\-include $(PACKAGE)/GNUmakefile > $(PACKAGE)/GNUmakefile2 + @mv $(PACKAGE)/GNUmakefile2 $(PACKAGE)/GNUmakefile + @mv $< $(PACKAGE) + @tar cf $(PACKAGE_SRC).tar $(PACKAGE) + @zip -r $(PACKAGE_SRC).zip $(PACKAGE) + @rm -Rf $(PACKAGE) + +$(PACKAGE_BIN).tar: clean $(uhub_BINARY) + @if [ -d $(PACKAGE) ]; then rm -Rf $(PACKAGE); fi + @svn export . $(PACKAGE) > /dev/null + @rm -Rf $(PACKAGE)/src $(PACKAGE)/autotest $(PACKAGE)/*akefile $(PACKAGE)/$(LIBUHUB) $(PACKAGE)/release_*.mk $(PACKAGE)/version.h + @cp $(uhub_BINARY) $(PACKAGE) + @tar cf $@ $(PACKAGE) + @rm -Rf $(PACKAGE) + +$(PACKAGE_BIN).zip: clean $(uhub_BINARY) + @if [ -d $(PACKAGE) ]; then rm -Rf $(PACKAGE); fi + @svn export . $(PACKAGE) > /dev/null + @rm -Rf $(PACKAGE)/src $(PACKAGE)/autotest $(PACKAGE)/*akefile $(PACKAGE)/$(LIBUHUB) $(PACKAGE)/release_*.mk $(PACKAGE)/version.h + @cp $(uhub_BINARY) $(PACKAGE) + @zip -r $@ $(PACKAGE) + @rm -Rf $(PACKAGE) + +$(PACKAGE_SRC).tar.gz: $(PACKAGE_SRC).tar + +$(PACKAGE_SRC).tar.bz2: $(PACKAGE_SRC).tar + +$(PACKAGE_BIN).tar.gz: $(PACKAGE_BIN).tar + +$(PACKAGE_BIN).tar.bz2: $(PACKAGE_BIN).tar + +snapshot: tarballs + @mv $(PACKAGE_SRC).tar.gz uhub-snapshot-$(SNAPSHOT).tar.gz + @rm $(PACKAGE_SRC).tar.bz2 + +publish-snapshot: snapshot + @scp -q uhub-snapshot-$(SNAPSHOT).tar.gz $(URL_SNAPSHOT) + +publish: release + @scp -q $(PACKAGE_SRC).tar.gz $(PACKAGE_SRC).tar.bz2 $(PACKAGE_BIN).tar.gz $(PACKAGE_BIN).tar.bz2 ChangeLog-$(VERSION)-$(REVISION) $(URL_PUBLISH) + +tarballs: $(PACKAGE_SRC).tar.gz $(PACKAGE_SRC).tar.bz2 $(PACKAGE_SRC).zip + @rm $(PACKAGE_SRC).tar + +ifeq ($(WINDOWS), YES) +binaries: $(PACKAGE_BIN).tar.gz $(PACKAGE_BIN).tar.bz2 $(PACKAGE_BIN).zip + @rm $(PACKAGE_BIN).tar + +else +binaries: $(PACKAGE_BIN).tar.gz $(PACKAGE_BIN).tar.bz2 + @rm $(PACKAGE_BIN).tar +endif + +release: binaries tarballs changelog + +else + + +endif + diff --git a/src/adcconst.h b/src/adcconst.h new file mode 100644 index 0000000..3c7e8f2 --- /dev/null +++ b/src/adcconst.h @@ -0,0 +1,142 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#ifndef HAVE_UHUB_ADC_CONSTANTS_H +#define HAVE_UHUB_ADC_CONSTANTS_H + +typedef uint32_t sid_t; +typedef uint32_t fourcc_t; + +/* Internal uhub limit */ +#define MAX_ADC_CMD_LEN 4096 + +#define FOURCC(a,b,c,d) (fourcc_t) ((a << 24) | (b << 16) | (c << 8) | d) + +/* default welcome protocol support message, as sent by this server */ +#define ADC_PROTO_SUPPORT "ADBASE ADTIGR ADPING" + +/* Server sent commands */ +#define ADC_CMD_ISID FOURCC('I','S','I','D') +#define ADC_CMD_ISUP FOURCC('I','S','U','P') +#define ADC_CMD_IGPA FOURCC('I','G','P','A') +#define ADC_CMD_ISTA FOURCC('I','S','T','A') +#define ADC_CMD_IINF FOURCC('I','I','N','F') +#define ADC_CMD_IMSG FOURCC('I','M','S','G') +#define ADC_CMD_IQUI FOURCC('I','Q','U','I') + +/* Handshake and login/passwordstuff */ +#define ADC_CMD_HSUP FOURCC('H','S','U','P') +#define ADC_CMD_HPAS FOURCC('H','P','A','S') +#define ADC_CMD_HINF FOURCC('H','I','N','F') +#define ADC_CMD_BINF FOURCC('B','I','N','F') + +/* This is a Admin extension */ +#define ADC_CMD_HDSC FOURCC('H','D','S','C') + +/* Status/error messages */ +#define ADC_CMD_HSTA FOURCC('H','S','T','A') +#define ADC_CMD_DSTA FOURCC('D','S','T','A') + +/* searches */ +#define ADC_CMD_BSCH FOURCC('B','S','C','H') +#define ADC_CMD_DSCH FOURCC('D','S','C','H') +#define ADC_CMD_ESCH FOURCC('E','S','C','H') +#define ADC_CMD_FSCH FOURCC('F','S','C','H') +#define ADC_CMD_DRES FOURCC('D','R','E','S') + +/* connection setup */ +#define ADC_CMD_DCTM FOURCC('D','C','T','M') +#define ADC_CMD_DRCM FOURCC('D','R','C','M') +#define ADC_CMD_ECTM FOURCC('E','C','T','M') +#define ADC_CMD_ERCM FOURCC('E','R','C','M') + +/* chat messages */ +#define ADC_CMD_BMSG FOURCC('B','M','S','G') +#define ADC_CMD_DMSG FOURCC('D','M','S','G') +#define ADC_CMD_EMSG FOURCC('E','M','S','G') +#define ADC_CMD_FMSG FOURCC('F','M','S','G') + +/* disallowed messages */ +#define ADC_CMD_DINF FOURCC('D','I','N','F') +#define ADC_CMD_EINF FOURCC('E','I','N','F') +#define ADC_CMD_FINF FOURCC('F','I','N','F') + +/* Extension messages */ +#define ADC_CMD_HCHK FOURCC('H','C','H','K') + +#define ADC_INF_FLAG_IPV4_ADDR "I4" /* ipv4 address */ +#define ADC_INF_FLAG_IPV6_ADDR "I6" /* ipv6 address */ +#define ADC_INF_FLAG_IPV4_UDP_PORT "U4" /* port number */ +#define ADC_INF_FLAG_IPV6_UDP_PORT "U6" /* port number */ +#define ADC_INF_FLAG_CLIENT_TYPE "CT" /* client type */ +#define ADC_INF_FLAG_PRIVATE_ID "PD" /* private id, aka PID */ +#define ADC_INF_FLAG_CLIENT_ID "ID" /* client id, aka CID */ +#define ADC_INF_FLAG_NICK "NI" /* nick name */ +#define ADC_INF_FLAG_DESCRIPTION "DE" /* user description */ +#define ADC_INF_FLAG_USER_AGENT "VE" /* software version */ +#define ADC_INF_FLAG_SUPPORT "SU" /* support (extensions, feature cast) */ +#define ADC_INF_FLAG_SHARED_SIZE "SS" /* size of total files shared in bytes */ +#define ADC_INF_FLAG_SHARED_FILES "SF" /* number of files shared */ +#define ADC_INF_FLAG_UPLOAD_SPEED "US" /* maximum upload speed acheived in bytes/sec */ +#define ADC_INF_FLAG_DOWNLOAD_SPEED "DS" /* maximum download speed acheived in bytes/sec */ +#define ADC_INF_FLAG_UPLOAD_SLOTS "SL" /* maximum upload slots (concurrent uploads) */ +#define ADC_INF_FLAG_AUTO_SLOTS "AS" /* automatic slot if upload speed is less than this in bytes/sec */ +#define ADC_INF_FLAG_AUTO_SLOTS_MAX "AM" /* maximum number of automatic slots */ +#define ADC_INF_FLAG_COUNT_HUB_NORMAL "HN" /* user is logged into this amount of hubs as a normal user (guest) */ +#define ADC_INF_FLAG_COUNT_HUB_REGISTER "HR" /* user is logged into this amount of hubs as a registered user (password) */ +#define ADC_INF_FLAG_COUNT_HUB_OPERATOR "HO" /* user is logged into this amount of hubs as an operator */ +#define ADC_INF_FLAG_AWAY "AW" /* away flag, 1=away, 2=extended away */ +#define ADC_INF_FLAG_REFERER "RF" /* URL to referer in case of hub redirect */ +#define ADC_INF_FLAG_EMAIL "EM" /* E-mail address */ + +#define ADC_MSG_FLAG_ACTION "ME" /* message is an *action* message */ +#define ADC_MSG_FLAG_PRIVATE "PM" /* message is a private message */ + +#define ADC_SCH_FLAG_INCLUDE "AN" /* include given search term */ +#define ADC_SCH_FLAG_EXCLUDE "NO" /* exclude given serach term */ +#define ADC_SCH_FLAG_FILE_EXTENSION "EX" /* search only for files with the given file extension */ +#define ADC_SCH_FLAG_FILE_TYPE "TY" /* search only for files with this file type (separate type) */ +#define ADC_SCH_FLAG_LESS_THAN "LE" /* search for files with this size or less */ +#define ADC_SCH_FLAG_GREATER_THAN "GE" /* search for files with this size or greater */ +#define ADC_SCH_FLAG_EQUAL "EQ" /* search only for files with this exact size */ +#define ADC_SCH_FLAG_TOKEN "TO" /* use this token for search replies */ + +#define ADC_RES_FLAG_FILE_NAME "FN" /* file name */ +#define ADC_RES_FLAG_FILE_SIZE "SI" /* file size */ +#define ADC_RES_FLAG_UPLOAD_SLOTS "SL" /* number of upload slots available (if > 0, download is possible) */ +#define ADC_RES_FLAG_TOKEN "TO" /* token, same as the token in the search request */ + +#define ADC_QUI_FLAG_TIME_LEFT "TL" /* time in seconds before reconnect is allowed, or -1 forever */ +#define ADC_QUI_FLAG_MESSAGE "MS" /* kick/leave message */ +#define ADC_QUI_FLAG_DISCONNECT "DI" /* all further transfers with this user should be disconnected */ +#define ADC_QUI_FLAG_REDIRECT "RD" /* redirect to URL */ +#define ADC_QUI_FLAG_KICK_OPERATOR "ID" /* SID of operator who disconnected the user */ + +#define ADC_SUP_FLAG_ADD "AD" +#define ADC_SUP_FLAG_REMOVE "RM" + +#define ADC_CLIENT_TYPE_BOT "1" +#define ADC_CLIENT_TYPE_REGISTERED_USER "2" +#define ADC_CLIENT_TYPE_OPERATOR "4" +#define ADC_CLIENT_TYPE_SUPER_USER "8" +#define ADC_CLIENT_TYPE_ADMIN "16" /* hub owner */ +#define ADC_CLIENT_TYPE_HUB "32" /* the hub itself */ + + +#endif /* HAVE_UHUB_ADC_CONSTANTS_H */ diff --git a/src/adcrush.c b/src/adcrush.c new file mode 100644 index 0000000..4cd4fd5 --- /dev/null +++ b/src/adcrush.c @@ -0,0 +1,950 @@ +/** + * An ADC client emulator. + */ + +#include "uhub.h" + +#define ADC_CLIENTS_DEFAULT 100 +#define ADC_MAX_CLIENTS 25000 + +#define ADC_BUFSIZE 16384 +#define ADC_SIDSIZE 4 +#define ADC_CID_SIZE 39 + +#define BIG_BUFSIZE 131072 +#define TIGERSIZE 24 + +#define ADC_HANDSHAKE "HSUP ADBASE ADTIGR\n" +#define ADCRUSH "adcrush/0.2" +#define ADC_NICK "[BOT]adcrush" +#define ADC_DESC "crash\\stest\\sdummy" + +struct ADC_client; + +static void ADC_client_on_disconnected(struct ADC_client*); +static void ADC_client_on_connected(struct ADC_client*); +static void ADC_client_on_login(struct ADC_client*); +static void ADC_client_connect(struct ADC_client*); +static void ADC_client_disconnect(struct ADC_client*); +static int ADC_client_create(struct ADC_client* client, int num); +static void ADC_client_destroy(struct ADC_client* client); + +static int cfg_mode = 0; // See enum operationMode +static char* cfg_host = 0; +static int cfg_port = 0; +static int cfg_debug = 0; /* debug level */ +static int cfg_level = 1; /* activity level (0..3) */ +static int cfg_chat = 0; /* chat mode, allow sending chat messages */ +static int cfg_quiet = 0; /* quiet mode (no output) */ +static int cfg_clients = ADC_CLIENTS_DEFAULT; /* number of clients */ + +static int running = 1; + +static struct sockaddr_in saddr; + +enum commandMode +{ + cm_bcast = 0x01, /* B - broadcast */ + cm_dir = 0x02, /* D - direct message */ + cm_echo = 0x04, /* E - echo message */ + cm_fcast = 0x08, /* F - feature cast message */ + cm_c2h = 0x10, /* H - client to hub message */ + cm_h2c = 0x20, /* I - hub to client message */ + cm_c2c = 0x40, /* C - client to client message */ + cm_udp = 0x80, /* U - udp message (client to client) */ +}; + +enum commandValidity +{ + cv_protocol = 0x01, + cv_identify = 0x02, + cv_verify = 0x04, + cv_normal = 0x08, +}; + +enum protocolState +{ + ps_none = 0x00, /* none or disconnected */ + ps_conn = 0x01, /* connecting... */ + ps_protocol = 0x02, + ps_identify = 0x04, + ps_verify = 0x08, + ps_normal = 0x10, +}; + +enum operationMode +{ + mode_performance = 0x01, + mode_bugs = 0x02, + mode_security = 0x04, + mode_log = 0x08, +}; + +struct commandPattern +{ + unsigned char mode; /* see enum commandMode */ + char cmd[3]; + unsigned char validity; /* see enum commandValidity */ +}; + +const struct commandPattern patterns[] = +{ + { cm_c2h | cm_c2c | cm_h2c, "SUP", cv_protocol | cv_normal }, /* protocol support */ + { cm_bcast | cm_h2c | cm_c2c, "INF", cv_identify | cv_verify | cv_normal }, /* info message */ + { cm_bcast | cm_h2c | cm_c2c | cm_c2h | cm_udp, "STA", cv_protocol | cv_identify | cv_verify | cv_normal }, /* status message */ + { cm_bcast | cm_dir | cm_echo | cm_h2c, "MSG", cv_normal }, /* chat message */ + { cm_bcast | cm_dir | cm_echo | cm_fcast, "SCH", cv_normal }, /* search */ + { cm_dir | cm_udp, "RES", cv_normal }, /* search result */ + { cm_dir | cm_echo, "CTM", cv_normal }, /* connect to me */ + { cm_dir | cm_echo, "RCM", cv_normal }, /* reversed, connect to me */ + { cm_h2c, "QUI", cv_normal }, /* quit message */ + { cm_h2c, "GPA", cv_identify }, /* password request */ + { cm_c2h, "PAS", cv_verify } /* password response */ +}; + +#define MAX_CHAT_MSGS 35 +const char* chat_messages[MAX_CHAT_MSGS] = { + "hello", + "I'm an annoying robot, configured to chat in order to measure performance of the hub.", + "I apologize for the inconvenience.", + ".", + ":)", + "can anyone help me, pls?", + "wtf?", + "bullshit", + "resistance is futile.", + "You crossed the line first, sir. You squeezed them, you hammered them to the point of desperation. And in their desperation they turned to a man they didn't fully understand.", + "beam me up, scotty", + "morning", + "You know where Harvey is? You know who he is?", + "gtg", + "thanks", + "*punt*", + "*nudge*", + "that's ok", + "...anyway", + "hola", + "hey", + "hi", + "nevermind", + "i think so", + "dunno", + "debian ftw", + "oops", + "how do I search?", + "how do I enable active mode?", + "home, sweet home...", + "later", + "Good evening, ladies and gentlemen. We are tonight's entertainment! I only have one question. Where is Harvey Dent?", + "You know where I can find Harvey? I need to talk to him about something. Just something, a little.", + "We really should stop fighting, we'll miss the fireworks!", + "Wanna know how I got these scars?", +}; + +#define MAX_SEARCH_MSGS 10 +const char* search_messages[MAX_SEARCH_MSGS] = { + "ANmp3 TOauto", + "ANxxx TOauto", + "ANdivx TOauto", + "ANtest ANfoo TOauto", + "ANwmv TO1289718", + "ANbabe TO8981884", + "ANpr0n TOauto", + "ANmusic TOauto", + "ANvideo TOauto", + "ANburnout ANps3 TOauto", +}; + +struct ADC_client +{ + int sd; + int num; + sid_t sid; + enum protocolState state; + char info[ADC_BUFSIZE]; + char recvbuf[BIG_BUFSIZE]; + char sendbuf[BIG_BUFSIZE]; + size_t s_offset; + size_t r_offset; + struct event ev_read; + struct event ev_write; + struct event ev_timer; + size_t timeout; +}; + + + +static void bot_output(struct ADC_client* client, const char* format, ...) +{ + char logmsg[1024]; + va_list args; + va_start(args, format); + vsnprintf(logmsg, 1024, format, args); + va_end(args); + if (cfg_mode == mode_log) + { + fprintf(stdout, "%s\n", logmsg); + } + else + { + if (cfg_debug) + fprintf(stdout, "* [%4d] %s\n", client->num, logmsg); + } +} + +static void adc_cid_pid(struct ADC_client* client) +{ + char seed[64]; + char pid[64]; + char cid[64]; + uint64_t tiger_res1[3]; + uint64_t tiger_res2[3]; + + /* create cid+pid pair */ + memset(seed, 0, 64); + snprintf(seed, 64, ADCRUSH "%p/%d", client, (int) client->num); + + tiger((uint64_t*) seed, strlen(seed), tiger_res1); + base32_encode((unsigned char*) tiger_res1, TIGERSIZE, pid); + tiger((uint64_t*) tiger_res1, TIGERSIZE, tiger_res2); + base32_encode((unsigned char*) tiger_res2, TIGERSIZE, cid); + + cid[ADC_CID_SIZE] = 0; + pid[ADC_CID_SIZE] = 0; + + strcat(client->info, " PD"); + strcat(client->info, pid); + strcat(client->info, " ID"); + strcat(client->info, cid); +} + +static size_t get_wait_rand(size_t max) +{ + static size_t next = 0; + if (next == 0) next = (size_t) time(0); + next = (next * 1103515245) + 12345; + return ((size_t )(next / 65536) % max); +} + +static void client_reschedule_timeout(struct ADC_client* client) +{ + size_t next_timeout = 0; + struct timeval timeout = { 0, 0 }; + + switch (client->state) + { + case ps_conn: next_timeout = 30; break; + case ps_protocol: next_timeout = 30; break; + case ps_identify: next_timeout = 30; break; + case ps_verify: next_timeout = 30; break; + case ps_normal: next_timeout = 120; break; + case ps_none: next_timeout = 120; break; + } + + if (client->state == ps_normal || client->state == ps_none) + { + switch (cfg_level) + { + case 0: /* polite */ + next_timeout *= 4; + break; + + case 1: /* normal */ + break; + + case 2: /* aggressive */ + next_timeout /= 8; + break; + + case 3: /* excessive */ + next_timeout /= 16; + + case 4: /* excessive */ + next_timeout /= 32; + } + + } + + + if (client->state == ps_conn) + client->timeout = MAX(next_timeout, 1); + else + client->timeout = get_wait_rand(MAX(next_timeout, 1)); + + if (!client->timeout) client->timeout++; + + timeout.tv_sec = (time_t) client->timeout; + evtimer_add(&client->ev_timer, &timeout); +} + +static void set_state_timeout(struct ADC_client* client, enum protocolState state) +{ + client->state = state; + client_reschedule_timeout(client); +} + +static void send_client(struct ADC_client* client, char* msg) +{ + int ret = net_send(client->sd, msg, strlen(msg), UHUB_SEND_SIGNAL); + + if (cfg_debug > 1) + { + char* dump = strdup(msg); + dump[strlen(msg) - 1] = 0; + bot_output(client, "- SEND: '%s'", dump); + free(dump); + } + + if (ret != strlen(msg)) + { + if (ret == -1) + { + if (net_error() != EWOULDBLOCK) + ADC_client_on_disconnected(client); + } + else + { + /* FIXME: Not all data sent! */ + printf("ret (%d) != msg->length (%d)\n", ret, (int) strlen(msg)); + } + } +} + + +static void ADC_client_on_connected(struct ADC_client* client) +{ + send_client(client, ADC_HANDSHAKE); + set_state_timeout(client, ps_protocol); + bot_output(client, "connected."); +} + +static void ADC_client_on_disconnected(struct ADC_client* client) +{ + event_del(&client->ev_read); + event_del(&client->ev_write); + + net_close(client->sd); + client->sd = -1; + + bot_output(client, "disconnected."); + set_state_timeout(client, ps_none); +} + +static void ADC_client_on_login(struct ADC_client* client) +{ + bot_output(client, "logged in."); + set_state_timeout(client, ps_normal); +} + + +static void send_client_info(struct ADC_client* client) +{ + client->info[0] = 0; + strcat(client->info, "BINF "); + strcat(client->info, sid_to_string(client->sid)); + strcat(client->info, " NI" ADC_NICK); + if (cfg_clients > 1) + { + strcat(client->info, "_"); + strcat(client->info, uhub_itoa(client->num)); + } + strcat(client->info, " VE" ADCRUSH); + strcat(client->info, " DE" ADC_DESC); + strcat(client->info, " I40.0.0.0"); + strcat(client->info, " EMuhub@extatic.org"); + strcat(client->info, " SL3"); + strcat(client->info, " HN1"); + strcat(client->info, " HR1"); + strcat(client->info, " HO1"); + + adc_cid_pid(client); + + strcat(client->info, "\n"); + + send_client(client, client->info); +} + +static void perf_result(struct ADC_client* client, sid_t target, const char* what, const char* token); + +static int recv_client(struct ADC_client* client) +{ + ssize_t size = 0; + if (cfg_mode != mode_performance || (cfg_mode == mode_performance && (get_wait_rand(100) < (90 - (15 * cfg_level))))) + { + size = net_recv(client->sd, &client->recvbuf[client->r_offset], ADC_BUFSIZE - client->r_offset, 0); + } + else + { + if (get_wait_rand(1000) == 99) + return -1; /* Can break tings badly! :-) */ + else + return 0; + } + if (size == 0 || ((size == -1 && net_error() != EWOULDBLOCK))) + return -1; + client->recvbuf[client->r_offset + size] = 0; + + char* start = client->recvbuf; + char* pos; + char* lastPos; + while ((pos = strchr(start, '\n'))) + { + lastPos = pos; + pos[0] = 0; + + if (cfg_debug > 1) + { + bot_output(client, "- RECV: '%s'", start); + } + + fourcc_t cmd = 0; + if (strlen(start) < 4) + { + bot_output(client, "Unexpected response from hub: '%s'", start); + start = &pos[1]; + continue; + } + + cmd = FOURCC(start[0], start[1], start[2], start[3]); + + switch (cmd) + { + case ADC_CMD_ISUP: + break; + + case ADC_CMD_ISID: + if (client->state == ps_protocol) + { + client->sid = string_to_sid(&start[5]); + client->state = ps_identify; + send_client_info(client); + + } + break; + + case ADC_CMD_IINF: + break; + + case ADC_CMD_BSCH: + case ADC_CMD_FSCH: + { + if (get_wait_rand(100) > (90 - (10 * cfg_level)) && cfg_mode == mode_performance) + { + sid_t target = string_to_sid(&start[5]); + const char* what = strstr(&start[5], " AN"); + const char* token = strstr(&start[5], " TO"); + char* split = 0; + if (!token || !what) break; + + token += 3; + what += 3; + + split = strchr(what, ' '); + if (!split) break; + else split[0] = '0'; + + split = strchr(token, ' '); + if (split) split[0] = '0'; + + perf_result(client, target, what, token); + + } + break; + } + case ADC_CMD_BINF: + { + if (strlen(start) > 9) + { + char t = start[9]; start[9] = 0; sid_t sid = string_to_sid(&start[5]); start[9] = t; + + if (sid == client->sid) + { + if (client->state == ps_verify || client->state == ps_identify) + { + ADC_client_on_login(client); + } + } + } + break; + } + + case ADC_CMD_ISTA: + if (strncmp(start, "ISTA 000", 8)) + { + bot_output(client, "status: '%s'\n", (start + 9)); + } + break; + + default: + break; + } + + start = &pos[1]; + } + + client->r_offset = strlen(lastPos); + memmove(client->recvbuf, lastPos, strlen(lastPos)); + memset(&client->recvbuf[client->r_offset], 0, ADC_BUFSIZE-client->r_offset); + + + return 0; +} + +void ADC_client_connect(struct ADC_client* client) +{ + struct timeval timeout = { TIMEOUT_IDLE, 0 }; + net_connect(client->sd, (struct sockaddr*) &saddr, sizeof(struct sockaddr_in)); + set_state_timeout(client, ps_conn); + event_add(&client->ev_read, &timeout); + event_add(&client->ev_write, &timeout); + bot_output(client, "connecting..."); +} + +void ADC_client_wait_connect(struct ADC_client* client) +{ + set_state_timeout(client, ps_none); + +} + + + +void ADC_client_disconnect(struct ADC_client* client) +{ + if (client->sd != -1) + { + net_close(client->sd); + client->sd = -1; + event_del(&client->ev_read); + event_del(&client->ev_write); + bot_output(client, "disconnected."); + + if (running) + { + ADC_client_destroy(client); + ADC_client_create(client, client->num); + ADC_client_connect(client); + } + + } +} + +static void perf_chat(struct ADC_client* client, int priv) +{ + char buf[1024] = { 0, }; + size_t r = get_wait_rand(MAX_CHAT_MSGS-1); + char* msg = adc_msg_escape(chat_messages[r]); + + if (priv) + { + strcat(buf, "EMSG "); + strcat(buf, sid_to_string(client->sid)); + strcat(buf, " "); + strcat(buf, sid_to_string(client->sid)); + } + else + { + strcat(buf, "BMSG "); + strcat(buf, sid_to_string(client->sid)); + } + strcat(buf, " "); + + strcat(buf, msg); + hub_free(msg); + + strcat(buf, "\n"); + send_client(client, buf); +} + +static void perf_search(struct ADC_client* client) +{ + char buf[1024] = { 0, }; + size_t r = get_wait_rand(MAX_SEARCH_MSGS-1); + size_t pst = get_wait_rand(100); + + if (pst > 80) + { + strcat(buf, "FSCH "); + strcat(buf, sid_to_string(client->sid)); + strcat(buf, " +TCP4 "); + } + else + { + strcat(buf, "BSCH "); + strcat(buf, sid_to_string(client->sid)); + strcat(buf, " "); + } + strcat(buf, search_messages[r]); + strcat(buf, "\n"); + send_client(client, buf); +} + +static void perf_result(struct ADC_client* client, sid_t target, const char* what, const char* token) +{ + char buf[1024] = { 0, }; + strcat(buf, "DRES "); + strcat(buf, sid_to_string(client->sid)); + strcat(buf, " "); + strcat(buf, sid_to_string(target)); + strcat(buf, " FN" "test/"); + strcat(buf, what); + strcat(buf, ".dat"); + strcat(buf, " SL" "0"); + strcat(buf, " SI" "908987128912"); + strcat(buf, " TR" "5T6YJYKO3WECS52BKWVSOP5VUG4IKNSZBZ5YHBA"); + strcat(buf, " TO"); + strcat(buf, token); + strcat(buf, "\n"); + send_client(client, buf); +} + +static void perf_ctm(struct ADC_client* client) +{ + char buf[1024] = { 0, }; + strcat(buf, "DCTM "); + strcat(buf, sid_to_string(client->sid)); + strcat(buf, " "); + strcat(buf, sid_to_string(client->sid)); + strcat(buf, " "); + strcat(buf, "ADC/1.0"); + strcat(buf, " TOKEN111"); + strcat(buf, sid_to_string(client->sid)); + strcat(buf, "\n"); + send_client(client, buf); +} + + +static void perf_update(struct ADC_client* client) +{ + char buf[1024] = { 0, }; + int n = (int) get_wait_rand(10)+1; + + strcat(buf, "BINF "); + strcat(buf, sid_to_string(client->sid)); + strcat(buf, " HN"); + strcat(buf, uhub_itoa(n)); + + strcat(buf, "\n"); + send_client(client, buf); +} + + +static void perf_normal_action(struct ADC_client* client) +{ + size_t r = get_wait_rand(5); + size_t p = get_wait_rand(100); + + switch (r) + { + case 0: + if (p > (90 - (10 * cfg_level))) + { + if (cfg_debug > 1) bot_output(client, "timeout -> disconnect"); + ADC_client_disconnect(client); + } + break; + + case 1: + if (cfg_chat) + { + if (cfg_debug > 1) bot_output(client, "timeout -> chat"); + perf_chat(client, 0); + + } + break; + + case 2: + if (cfg_debug > 1) bot_output(client, "timeout -> search"); + perf_search(client); + break; + + case 3: + if (cfg_debug > 1) bot_output(client, "timeout -> update"); + perf_update(client); + break; + + case 4: + if (cfg_debug > 1) bot_output(client, "timeout -> privmsg"); + perf_chat(client, 1); + break; + + case 5: + if (cfg_debug > 1) bot_output(client, "timeout -> ctm/rcm"); + perf_ctm(client); + break; + + } + + client_reschedule_timeout(client); +} + +void event_callback(int fd, short ev, void *arg) +{ + struct ADC_client* client = (struct ADC_client*) arg; + + if (ev & EV_READ) + { + if (recv_client(client) == -1) + { + ADC_client_on_disconnected(client); + } + } + + if (ev & EV_TIMEOUT) + { + if (client->state == ps_none) + { + if (client->sd == -1) + { + ADC_client_create(client, client->num); + } + + ADC_client_connect(client); + } + + if (fd == -1) + { + if (client->state == ps_normal && cfg_mode == mode_performance) + { + perf_normal_action(client); + } + } + } + + if (ev & EV_WRITE) + { + if (client->state == ps_conn) + { + ADC_client_on_connected(client); + } + else + { + /* FIXME: Call send again */ + } + + } +} + +int ADC_client_create(struct ADC_client* client, int num) +{ + struct timeval timeout = { 0, 0 }; + + memset(client, 0, sizeof(struct ADC_client)); + client->num = num; + + client->sd = net_socket_create(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (client->sd == -1) return -1; + + event_set(&client->ev_write, client->sd, EV_WRITE, event_callback, client); + event_set(&client->ev_read, client->sd, EV_READ | EV_PERSIST, event_callback, client); + + net_set_nonblocking(client->sd, 1); + + timeout.tv_sec = client->timeout; + evtimer_set(&client->ev_timer, event_callback, client); + + set_state_timeout(client, ps_none); + + return 0; +} + +void ADC_client_destroy(struct ADC_client* client) +{ + ADC_client_disconnect(client); + evtimer_del(&client->ev_timer); +} + + +void runloop(size_t clients) +{ + size_t n = 0; + struct ADC_client* client[ADC_MAX_CLIENTS]; + + for (n = 0; n < clients; n++) + { + struct ADC_client* c = malloc(sizeof(struct ADC_client)); + client[n] = c; + + ADC_client_create(c, n); + if (n == 0) + { + ADC_client_connect(c); + } + else + { + ADC_client_wait_connect(c); + } + } + + event_dispatch(); + + for (n = 0; n < clients; n++) + { + ADC_client_destroy(client[n]); + free(client[n]); + } +} + +static void print_version() +{ + printf(ADCRUSH "\n"); + printf("Copyright (C) 2008-2009, Jan Vidar Krey\n"); + printf("\n"); +} + +static void print_usage(const char* program) +{ + print_version(); + + printf("Usage: %s (adc://:) [options]\n", program); + + printf("\n"); + printf(" Modes\n"); + printf(" perf Do performance testing using multiple clients\n"); + printf(" bugs Bugs mode, use fuzzer to construct pseudo-random commands.\n"); + printf(" security Perform security tests for the hub.\n"); + printf(" log Connect one client to the hub and log the output hub.\n"); + + printf("\n"); + printf(" General options\n"); + printf(" -c Allow broadcasting chat messages.\n"); + printf(" -d Enable debug output.\n"); + printf(" -q Quiet mode (no output).\n"); + + printf("\n"); + printf(" Performance options:\n"); + printf(" -l <0-3> Level: 0=polite, 1=normal (default), 2=aggressive, 3=excessive.\n"); + printf(" -n Number of concurrent connections\n"); + + printf("\n"); + + exit(0); +} + + +int set_defaults() +{ + switch (cfg_mode) + { + case mode_performance: + break; + case mode_bugs: + break; + case mode_security: + break; + case mode_log: + cfg_quiet = 0; + cfg_debug = 2; + cfg_clients = 1; + break; + } + return 1; +} + +int parse_mode(const char* arg) +{ + cfg_mode = 0; + + if (!strcmp(arg, "perf")) + cfg_mode = mode_performance; + else if (!strcmp(arg, "bugs")) + cfg_mode = mode_bugs; + else if (!strcmp(arg, "security")) + cfg_mode = mode_security; + else if (!strcmp(arg, "log")) + cfg_mode = mode_log; + + return cfg_mode; +} + +int parse_address(const char* arg) +{ + char* split; + struct hostent* dns; + struct in_addr* addr; + + if (!arg) + return 0; + + if (strlen(arg) < 9) + return 0; + + if (strncmp(arg, "adc://", 6)) + return 0; + + split = strrchr(arg+6, ':'); + if (split == 0 || strlen(split) < 2 || strlen(split) > 6) + return 0; + + cfg_port = strtol(split+1, NULL, 10); + if (cfg_port <= 0 || cfg_port > 65535) + return 0; + + split[0] = 0; + + dns = gethostbyname(arg+6); + if (dns) + { + addr = (struct in_addr*) dns->h_addr_list[0]; + cfg_host = strdup(inet_ntoa(*addr)); + } + + if (!cfg_host) + return 0; + + return 1; +} + +int parse_arguments(int argc, char** argv) +{ + int ok = 1; + int opt; + for (opt = 3; opt < argc; opt++) + { + if (!strcmp(argv[opt], "-c")) + cfg_chat = 1; + else if (!strncmp(argv[opt], "-d", 2)) + cfg_debug += strlen(argv[opt]) - 1; + else if (!strcmp(argv[opt], "-q")) + cfg_quiet = 1; + else if (!strcmp(argv[opt], "-l") && (++opt) < argc) + { + cfg_level = MIN(MAX(uhub_atoi(argv[opt]), 0), 3); + } + else if (!strcmp(argv[opt], "-n") && (++opt) < argc) + { + cfg_clients = MIN(MAX(uhub_atoi(argv[opt]), 1), ADC_MAX_CLIENTS); + } + } + return ok; +} + + +void parse_command_line(int argc, char** argv) +{ + if (argc < 2 || + !parse_mode(argv[1]) || + !set_defaults() || + !parse_address(argv[2]) || + !parse_arguments(argc, argv)) + { + print_usage(argv[0]); + } +} + +int main(int argc, char** argv) +{ + parse_command_line(argc, argv); + + net_initialize(); + + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_port = htons(cfg_port); + net_string_to_address(AF_INET, cfg_host, &saddr.sin_addr); + + runloop(cfg_clients); + net_shutdown(); + + return 0; +} + diff --git a/src/auth.c b/src/auth.c new file mode 100644 index 0000000..ab70c18 --- /dev/null +++ b/src/auth.c @@ -0,0 +1,555 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#include "uhub.h" + +#define ACL_ADD_USER(S, L, V) do { ret = check_cmd_user(S, V, L, line, line_count); if (ret != 0) return ret; } while(0) +#define ACL_ADD_BOOL(S, L) do { ret = check_cmd_bool(S, L, line, line_count); if (ret != 0) return ret; } while(0) +#define ACL_ADD_ADDR(S, L) do { ret = check_cmd_addr(S, L, line, line_count); if (ret != 0) return ret; } while(0) + +static const char* get_user_credential_string(enum user_credentials cred) +{ + switch (cred) + { + case cred_none: return "none"; + case cred_bot: return "bot"; + case cred_guest: return "guest"; + case cred_user: return "user"; + case cred_operator: return "operator"; + case cred_super: return "super"; + case cred_admin: return "admin"; + case cred_link: return "link"; + } + + return ""; +}; + +static int check_cmd_bool(const char* cmd, struct linked_list* list, char* line, int line_count) +{ + char* data; + char* data_extra; + + if (!strncmp(line, cmd, strlen(cmd))) + { + data = &line[strlen(cmd)]; + data_extra = 0; + data[0] = '\0'; + data++; + + data = strip_white_space(data); + if (!strlen(data)) + { + hub_log(log_fatal, "ACL parse error on line %d", line_count); + return -1; + } + + list_append(list, hub_strdup(data)); + hub_log(log_debug, "ACL: Deny access for: '%s' (%s)", data, cmd); + return 1; + } + return 0; +} + +static int check_cmd_user(const char* cmd, int status, struct linked_list* list, char* line, int line_count) +{ + char* data; + char* data_extra; + struct user_access_info* info = 0; + + if (!strncmp(line, cmd, strlen(cmd))) + { + data = &line[strlen(cmd)]; + data_extra = 0; + data[0] = '\0'; + data++; + + data = strip_white_space(data); + if (!strlen(data)) + { + hub_log(log_fatal, "ACL parse error on line %d", line_count); + return -1; + } + + info = hub_malloc_zero(sizeof(struct user_access_info)); + + if (!info) + { + hub_log(log_error, "ACL parse error. Out of memory!"); + return -1; + } + + if (strncmp(cmd, "user_", 5) == 0) + { + data_extra = strrchr(data, ':'); + if (data_extra) + { + data_extra[0] = 0; + data_extra++; + } + } + + info->username = hub_strdup(data); + info->password = data_extra ? hub_strdup(data_extra) : 0; + info->status = status; + list_append(list, info); + hub_log(log_debug, "ACL: Added user '%s' (%s)", info->username, get_user_credential_string(info->status)); + return 1; + } + return 0; +} + + +static void add_ip_range(struct linked_list* list, struct ip_ban_record* info) +{ + char buf1[INET6_ADDRSTRLEN+1]; + char buf2[INET6_ADDRSTRLEN+1]; + + if (info->lo.af == AF_INET) + { + net_address_to_string(AF_INET, &info->lo.internal_ip_data.in.s_addr, buf1, INET6_ADDRSTRLEN); + net_address_to_string(AF_INET, &info->hi.internal_ip_data.in.s_addr, buf2, INET6_ADDRSTRLEN); + } + else if (info->lo.af == AF_INET6) + { + net_address_to_string(AF_INET6, &info->lo.internal_ip_data.in6, buf1, INET6_ADDRSTRLEN); + net_address_to_string(AF_INET6, &info->hi.internal_ip_data.in6, buf2, INET6_ADDRSTRLEN); + } + hub_log(log_debug, "ACL: Deny access for: %s-%s", buf1, buf2); + + list_append(list, info); +} + + +static int check_ip_range(const char* lo, const char* hi, struct ip_ban_record* info) +{ + int ret1, ret2; + + if ((ip_is_valid_ipv4(lo) && ip_is_valid_ipv4(hi)) || + (ip_is_valid_ipv6(lo) && ip_is_valid_ipv6(hi))) + { + ret1 = ip_convert_to_binary(lo, &info->lo); + ret2 = ip_convert_to_binary(hi, &info->hi); + if (ret1 == -1 || ret2 == -1 || ret1 != ret2) + { + return -1; + } + return 0; + } + return -1; +} + + +static int check_ip_mask(const char* text_addr, int bits, struct ip_ban_record* info) +{ + hub_log(log_debug, "ACL: Deny access for: %s/%d", text_addr, bits); + + if (ip_is_valid_ipv4(text_addr) || + ip_is_valid_ipv6(text_addr)) + { + struct ip_addr_encap addr; + struct ip_addr_encap mask1; + struct ip_addr_encap mask2; + int af = ip_convert_to_binary(text_addr, &addr); /* 192.168.1.2 */ + int maxbits = af == AF_INET6 ? 128 : 32; + ip_mask_create_left(af, bits, &mask1); /* 255.255.255.0 */ + ip_mask_create_right(af, maxbits - bits, &mask2); /* 0.0.0.255 */ + ip_mask_apply_AND(&addr, &mask1, &info->lo); /* 192.168.1.0 */ + ip_mask_apply_OR(&info->lo, &mask2, &info->hi); /* 192.168.1.255 */ + return 0; + } + return -1; +} + + +static int check_cmd_addr(const char* cmd, struct linked_list* list, char* line, int line_count) +{ + char* data1; + char* data2; + struct ip_ban_record* info = 0; + int cidr_bits = 0; + + if (!strncmp(line, cmd, strlen(cmd))) + { + data1 = &line[strlen(cmd)]; + data2 = 0; + data1[0] = '\0'; + data1++; + + data1 = strip_white_space(data1); + if (!strlen(data1)) + { + hub_log(log_fatal, "ACL parse error on line %d", line_count); + return -1; + } + + info = hub_malloc_zero(sizeof(struct ip_ban_record)); + + if (!info) + { + hub_log(log_error, "ACL parse error. Out of memory!"); + return -1; + } + + /* Extract IP-range */ + data2 = strrchr(data1, '-'); + if (data2) + { + cidr_bits = -1; + data2[0] = 0; + data2++; + + if (check_ip_range(data1, data2, info) == -1) + { + hub_free(info); + return 0; + } + + add_ip_range(list, info); + + return 1; + } + else + { + /* Extract IP-bitmask */ + data2 = strrchr(data1, '/'); + if (data2) + { + data2[0] = 0; + data2++; + cidr_bits = uhub_atoi(data2); + } + else + { + cidr_bits = 128; + } + + if (check_ip_mask(data1, cidr_bits, info) == -1) + { + hub_free(info); + return 0; + } + + add_ip_range(list, info); + + return 1; + } + } + return 0; +} + + + +static int acl_parse_line(char* line, int line_count, void* ptr_data) +{ + char* pos; + struct acl_handle* handle = (struct acl_handle*) ptr_data; + int ret; + + if ((pos = strchr(line, '#')) != NULL) + { + pos[0] = 0; + } + + if (strlen(line) == 0) + return 0; + +#ifdef ACL_DEBUG + hub_log(log_trace, "acl_parse_line(): '%s'", line); +#endif + line = strip_white_space(line); + + if (!strlen(line)) + { + hub_log(log_fatal, "ACL parse error on line %d", line_count); + return -1; + } + +#ifdef ACL_DEBUG + hub_log(log_trace, "acl_parse_line: '%s'", line); +#endif + + ACL_ADD_USER("bot", handle->users, cred_bot); + ACL_ADD_USER("user_admin", handle->users, cred_admin); + ACL_ADD_USER("user_super", handle->users, cred_super); + ACL_ADD_USER("user_op", handle->users, cred_operator); + ACL_ADD_USER("user_reg", handle->users, cred_user); + ACL_ADD_USER("link", handle->users, cred_link); + ACL_ADD_BOOL("deny_nick", handle->users_denied); + ACL_ADD_BOOL("ban_nick", handle->users_banned); + ACL_ADD_BOOL("ban_cid", handle->cids); + ACL_ADD_ADDR("deny_ip", handle->networks); + ACL_ADD_ADDR("nat_ip", handle->nat_override); + + hub_log(log_error, "Unknown ACL command on line %d: '%s'", line_count, line); + return -1; +} + + +int acl_initialize(struct hub_config* config, struct acl_handle* handle) +{ + int ret; + memset(handle, 0, sizeof(struct acl_handle)); + + handle->users = list_create(); + handle->users_denied = list_create(); + handle->users_banned = list_create(); + handle->cids = list_create(); + handle->networks = list_create(); + handle->nat_override = list_create(); + + if (!handle->users || !handle->cids || !handle->networks || !handle->users_denied || !handle->users_banned || !handle->nat_override) + { + hub_log(log_fatal, "acl_initialize: Out of memory"); + + list_destroy(handle->users); + list_destroy(handle->users_denied); + list_destroy(handle->users_banned); + list_destroy(handle->cids); + list_destroy(handle->networks); + list_destroy(handle->nat_override); + return -1; + } + + if (config) + { + if (strlen(config->file_acl) == 0) return 0; + + ret = file_read_lines(config->file_acl, handle, &acl_parse_line); + if (ret == -1) + return -1; + } + return 0; +} + + +static void acl_free_access_info(void* ptr) +{ + struct user_access_info* info = (struct user_access_info*) ptr; + if (info) + { + hub_free(info->username); + hub_free(info->password); + hub_free(info); + } +} + + +static void acl_free_ip_info(void* ptr) +{ + struct access_info* info = (struct access_info*) ptr; + if (info) + { + hub_free(info); + } +} + +int acl_shutdown(struct acl_handle* handle) +{ + if (handle->users) + { + list_clear(handle->users, &acl_free_access_info); + list_destroy(handle->users); + } + + if (handle->users_denied) + { + list_clear(handle->users_denied, &hub_free); + list_destroy(handle->users_denied); + } + + if (handle->users_banned) + { + list_clear(handle->users_banned, &hub_free); + list_destroy(handle->users_banned); + } + + + if (handle->cids) + { + list_clear(handle->cids, &hub_free); + list_destroy(handle->cids); + } + + if (handle->networks) + { + list_clear(handle->networks, &acl_free_ip_info); + list_destroy(handle->networks); + } + + if (handle->nat_override) + { + list_clear(handle->nat_override, &acl_free_ip_info); + list_destroy(handle->nat_override); + } + + + memset(handle, 0, sizeof(struct acl_handle)); + return 0; +} + + +struct user_access_info* acl_get_access_info(struct acl_handle* handle, const char* name) +{ + struct user_access_info* info = (struct user_access_info*) list_get_first(handle->users); + while (info) + { + if (strcasecmp(info->username, name) == 0) + { + return info; + } + info = (struct user_access_info*) list_get_next(handle->users); + } + return NULL; +} + +#define STR_LIST_CONTAINS(LIST, STR) \ + char* str = (char*) list_get_first(LIST); \ + while (str) \ + { \ + if (strcasecmp(str, STR) == 0) \ + return 1; \ + str = (char*) list_get_next(LIST); \ + } \ + return 0 + +int acl_is_cid_banned(struct acl_handle* handle, const char* data) +{ + if (!handle) return 0; + STR_LIST_CONTAINS(handle->cids, data); +} + +int acl_is_user_banned(struct acl_handle* handle, const char* data) +{ + if (!handle) return 0; + STR_LIST_CONTAINS(handle->users_banned, data); +} + +int acl_is_user_denied(struct acl_handle* handle, const char* data) +{ + if (!handle) return 0; + STR_LIST_CONTAINS(handle->users_denied, data); +} + +int acl_is_ip_banned(struct acl_handle* handle, const char* ip_address) +{ + struct ip_addr_encap raw; + struct ip_ban_record* info = (struct ip_ban_record*) list_get_first(handle->networks); + ip_convert_to_binary(ip_address, &raw); + + while (info) + { + if (acl_check_ip_range(&raw, info)) + { + return 1; + } + info = (struct ip_ban_record*) list_get_next(handle->networks); + } + return 0; +} + +int acl_is_ip_nat_override(struct acl_handle* handle, const char* ip_address) +{ + struct ip_addr_encap raw; + struct ip_ban_record* info = (struct ip_ban_record*) list_get_first(handle->nat_override); + ip_convert_to_binary(ip_address, &raw); + + while (info) + { + if (acl_check_ip_range(&raw, info)) + { + return 1; + } + info = (struct ip_ban_record*) list_get_next(handle->nat_override); + } + return 0; +} + + +int acl_check_ip_range(struct ip_addr_encap* addr, struct ip_ban_record* info) +{ + return (addr->af == info->lo.af && ip_compare(&info->lo, addr) <= 0 && ip_compare(addr, &info->hi) <= 0); +} + +/* + * This will generate the same challenge to the same user, always. + * The challenge is made up of the time of the user connected + * seconds since the unix epoch (modulus 1 million) + * and the SID of the user (0-1 million). + */ +const char* password_generate_challenge(struct user* user) +{ + char buf[32]; + uint64_t tiger_res[3]; + static char tiger_buf[MAX_CID_LEN+1]; + + snprintf(buf, 32, "%d%d%d", (int) user->tm_connected, (int) user->id.sid, (int) user->sd); + + tiger((uint64_t*) buf, strlen(buf), (uint64_t*) tiger_res); + base32_encode((unsigned char*) tiger_res, TIGERSIZE, tiger_buf); + tiger_buf[MAX_CID_LEN] = 0; + +#ifdef ACL_DEBUG + hub_log(log_trace, "Generating challenge for user %s: '%s'", user->id.nick, tiger_buf); +#endif + return (const char*) tiger_buf; +} + + +int password_verify(struct user* user, const char* password) +{ + char buf[1024]; + struct user_access_info* access; + const char* challenge; + char raw_challenge[64]; + char password_calc[64]; + uint64_t tiger_res[3]; + + if (!password || !user || strlen(password) != MAX_CID_LEN) + return password_invalid; + + access = acl_get_access_info(user->hub->acl, user->id.nick); + if (!access || !access->password) + return password_invalid; + + if (TIGERSIZE+strlen(access->password) >= 1024) + return password_invalid; + + challenge = password_generate_challenge(user); + + base32_decode(challenge, (unsigned char*) raw_challenge, MAX_CID_LEN); + + memcpy(&buf[0], (char*) access->password, strlen(access->password)); + memcpy(&buf[strlen(access->password)], raw_challenge, TIGERSIZE); + + tiger((uint64_t*) buf, TIGERSIZE+strlen(access->password), (uint64_t*) tiger_res); + base32_encode((unsigned char*) tiger_res, TIGERSIZE, password_calc); + password_calc[MAX_CID_LEN] = 0; + +#ifdef ACL_DEBUG + hub_log(log_trace, "Checking password %s against %s", password, password_calc); +#endif + if (strcasecmp(password, password_calc) == 0) + { + return password_ok; + } + return password_invalid; +} + + + diff --git a/src/auth.h b/src/auth.h new file mode 100644 index 0000000..3d5da8a --- /dev/null +++ b/src/auth.h @@ -0,0 +1,91 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#ifndef HAVE_UHUB_ACL_H +#define HAVE_UHUB_ACL_H + +struct hub_config; +struct user; +struct ip_addr_encap; + +enum password_status +{ + password_invalid = 0, + password_ok = 1, +}; + +enum acl_status +{ + acl_not_found = 0, + acl_found = 1, +}; + +enum user_credentials +{ + cred_none, /**<<< "User has no credentials (not yet logged in)" */ + cred_bot, /**<<< "User is a robot" */ + cred_guest, /**<<< "User is a guest (unregistered user)" */ + cred_user, /**<<< "User is identified as a registered user" */ + cred_operator, /**<<< "User is identified as a hub operator" */ + cred_super, /**<<< "User is a super user" (not used) */ + cred_admin, /**<<< "User is identified as a hub administrator/owner" */ + cred_link, /**<<< "User is a link (not used currently)" */ +}; + +struct user_access_info +{ + char* username; /* name of user, cid or IP range */ + char* password; /* password */ + enum user_credentials status; +}; + +struct ip_ban_record +{ + struct ip_addr_encap lo; + struct ip_addr_encap hi; +}; + +struct acl_handle +{ + struct linked_list* users; /* Known users. See enum user_status */ + struct linked_list* cids; /* Known CIDs */ + struct linked_list* networks; /* IP ranges, used for banning */ + struct linked_list* nat_override; /* IPs inside these ranges can provide their false IP. Use with care! */ + struct linked_list* users_banned; /* Users permanently banned */ + struct linked_list* users_denied; /* bad nickname */ +}; + + +extern int acl_initialize(struct hub_config* config, struct acl_handle* handle); +extern int acl_shutdown(struct acl_handle* handle); + +extern struct user_access_info* acl_get_access_info(struct acl_handle* handle, const char* name); +extern int acl_is_cid_banned(struct acl_handle* handle, const char* cid); +extern int acl_is_ip_banned(struct acl_handle* handle, const char* ip_address); +extern int acl_is_ip_nat_override(struct acl_handle* handle, const char* ip_address); + +extern int acl_is_user_banned(struct acl_handle* handle, const char* name); +extern int acl_is_user_denied(struct acl_handle* handle, const char* name); + +extern int acl_check_ip_range(struct ip_addr_encap* addr, struct ip_ban_record* info); + +extern const char* password_generate_challenge(struct user* user); +extern int password_verify(struct user* user, const char* password); + +#endif /* HAVE_UHUB_ACL_H */ diff --git a/src/commands.c b/src/commands.c new file mode 100644 index 0000000..3c21d74 --- /dev/null +++ b/src/commands.c @@ -0,0 +1,127 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#include "uhub.h" + +static int command_access_denied(struct user* user) +{ + struct adc_message* command; + char* buffer = adc_msg_escape("Access denied."); + command = adc_msg_construct(ADC_CMD_IMSG, strlen(buffer) + 6); + adc_msg_add_argument(command, buffer); + route_to_user(user, command); + adc_msg_free(command); + hub_free(buffer); + return 0; +} + + +static int command_stats(struct user* user, const char* message) +{ + struct adc_message* command; + + if (user->credentials < cred_super) + return command_access_denied(user); + + char temp[64]; + snprintf(temp, 64, "*** Stats: %u users, peak %u", (unsigned int) user->hub->users->count, (unsigned int) user->hub->users->count_peak); + char* buffer = adc_msg_escape(temp); + command = adc_msg_construct(ADC_CMD_IMSG, strlen(buffer) + 6); + adc_msg_add_argument(command, buffer); + route_to_user(user, command); + adc_msg_free(command); + hub_free(buffer); + return 0; +} + + +static int command_help(struct user* user, const char* message) +{ + struct adc_message* command; + char* buffer = adc_msg_escape( + "*** Available commands:\n" + "!help - Show this help message\n" + "!stats - Show hub stats (super)\n" + "!version - Show this help message\n" + "!uptime - Display hub uptime\n" + "!kick - Kick user (operator)\n" + ); + + command = adc_msg_construct(ADC_CMD_IMSG, strlen(buffer) + 6); + adc_msg_add_argument(command, buffer); + route_to_user(user, command); + adc_msg_free(command); + hub_free(buffer); + return 0; +} + +static int command_uptime(struct user* user, const char* message) +{ + struct adc_message* command; + char temp[64]; + snprintf(temp, 64, "*** Uptime: %s seconds", uhub_itoa((int) difftime(time(0), user->hub->tm_started))); + char* buffer = adc_msg_escape(temp); + command = adc_msg_construct(ADC_CMD_IMSG, strlen(buffer) + 6); + adc_msg_add_argument(command, buffer); + route_to_user(user, command); + adc_msg_free(command); + hub_free(buffer); + return 0; +} + +static int command_kick(struct user* user, const char* message) +{ + struct adc_message* command; + + if (user->credentials < cred_operator) + return command_access_denied(user); + + char* buffer = adc_msg_escape("*** Kick not implemented!"); + command = adc_msg_construct(ADC_CMD_IMSG, strlen(buffer) + 6); + adc_msg_add_argument(command, buffer); + route_to_user(user, command); + adc_msg_free(command); + hub_free(buffer); + return 0; +} + +static int command_version(struct user* user, const char* message) +{ + struct adc_message* command; + char* buffer = adc_msg_escape("*** Powered by " PRODUCT "/" VERSION); + command = adc_msg_construct(ADC_CMD_IMSG, strlen(buffer) + 6); + adc_msg_add_argument(command, buffer); + route_to_user(user, command); + adc_msg_free(command); + hub_free(buffer); + return 0; +} + +int command_dipatcher(struct user* user, const char* message) +{ + if (!strncmp(message, "!stats", 6)) command_stats(user, message); + else if (!strncmp(message, "!help", 5)) command_help(user, message); + else if (!strncmp(message, "!kick", 5)) command_kick(user, message); + else if (!strncmp(message, "!version", 8)) command_version(user, message); + else if (!strncmp(message, "!uptime", 7)) command_uptime(user, message); + else + return 1; + return 0; +} + diff --git a/src/commands.h b/src/commands.h new file mode 100644 index 0000000..4c26c83 --- /dev/null +++ b/src/commands.h @@ -0,0 +1,35 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#include "uhub.h" + +#define CHAT_MSG_HANDLED 1 +#define CHAT_MSG_IGNORED 0 +#define CHAT_MSG_INVALID -1 + +typedef int (*plugin_event_chat_message)(struct hub_info*, struct user*, struct adc_message*); + +struct command_info +{ + const char* prefix; + enum user_credentials cred; + plugin_event_chat_message function; +}; + +int command_dipatcher(struct user* user, const char* message); diff --git a/src/config.c b/src/config.c new file mode 100644 index 0000000..e4be43a --- /dev/null +++ b/src/config.c @@ -0,0 +1,519 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + + +#include "uhub.h" + + +#ifndef INT_MAX +#define INT_MAX 0x7fffffff +#endif + +#ifndef INT_MIN +#define INT_MIN (-0x7fffffff - 1) +#endif + +#define CFG_APPLY_BOOLEAN(KEY, TARGET) \ + if (strcmp(KEY, key) == 0) \ + { \ + if (strlen(data) == 1 && (data[0] == '1')) TARGET = 1; \ + else if (strlen(data) == 1 && (data[0] == '0')) TARGET = 0; \ + else if (strncasecmp(data, "true", 4) == 0) TARGET = 1; \ + else if (strncasecmp(data, "false", 5) == 0) TARGET = 0; \ + else if (strncasecmp(data, "yes", 3) == 0) TARGET = 1; \ + else if (strncasecmp(data, "no", 2) == 0) TARGET = 0; \ + else if (strncasecmp(data, "on", 2) == 0) TARGET = 1; \ + else if (strncasecmp(data, "off", 3) == 0) TARGET = 0; \ + else\ + { \ + hub_log(log_fatal, "Configuration error on line %d: '%s' must be either '1' or '0'", line_count, key); \ + return -1; \ + } \ + TARGET |= 0x80000000; \ + return 0; \ + } + +#define CFG_APPLY_STRING(KEY, TARGET) \ + if (strcmp(KEY, key) == 0) \ + { \ + TARGET = hub_strdup(data); \ + return 0; \ + } + + +#define CFG_APPLY_INTEGER(KEY, TARGET) \ + if (strcmp(KEY, key) == 0) \ + { \ + char* endptr; \ + int val; \ + errno = 0; \ + val = strtol(data, &endptr, 10); \ + if (((errno == ERANGE && (val == INT_MAX || val == INT_MIN)) || (errno != 0 && val == 0)) || endptr == data /*|| endptr != &data[strlen(data)-1]*/) { \ + hub_log(log_fatal, "Configuration error on line %d: '%s' must be a number", line_count, key); \ + return -1; \ + } \ + TARGET = val; \ + return 0; \ + } + + +#define DEFAULT_STRING(KEY, VALUE) \ +{ \ + if (config->KEY == 0) \ + config->KEY = hub_strdup(VALUE); \ +} + +#define DEFAULT_INTEGER(KEY, VALUE) \ +{ \ + if (config->KEY == 0) \ + config->KEY = VALUE; \ +} + +#define DEFAULT_BOOLEAN(KEY, VALUE) \ +{ \ + if (config->KEY & 0x80000000) \ + { \ + config->KEY = config->KEY & 0x000000ff; \ + } \ + else \ + { \ + config->KEY = VALUE; \ + } \ +} + +#define GET_STR(NAME) CFG_APPLY_STRING ( #NAME , config->NAME ) +#define GET_INT(NAME) CFG_APPLY_INTEGER( #NAME , config->NAME ) +#define GET_BOOL(NAME) CFG_APPLY_BOOLEAN( #NAME , config->NAME ) +#define IGNORED(NAME) \ + if (strcmp(#NAME, key) == 0) \ + { \ + hub_log(log_warning, "Configuration option %s deprecated and ingnored.", key); \ + return 0; \ + } \ + +/* default configuration values */ +#define DEF_SERVER_BIND_ADDR "any" +#define DEF_SERVER_PORT 1511 +#define DEF_HUB_NAME "uhub" +#define DEF_HUB_DESCRIPTION "" +#define DEF_HUB_ENABLED 1 +#define DEF_FILE_ACL "" +#define DEF_FILE_MOTD "" +#define DEF_MAX_USERS 500 +#define DEF_MAX_RECV_BUFFER 4096 +#define DEF_MAX_SEND_BUFFER 131072 +#define DEF_MAX_SEND_BUFFER_SOFT 98304 +#define DEF_SHOW_BANNER 1 +#define DEF_REGISTERED_USERS_ONLY 0 +#define DEF_CHAT_ONLY 0 +#define DEF_CHAT_IS_PRIVILEGED 0 +#define DEF_LOW_BANDWIDTH_MODE 0 +#define DEF_LIMIT_MAX_HUBS_USER 0 +#define DEF_LIMIT_MAX_HUBS_REG 0 +#define DEF_LIMIT_MAX_HUBS_OP 0 +#define DEF_LIMIT_MAX_HUBS 0 +#define DEF_LIMIT_MIN_HUBS_USER 0 +#define DEF_LIMIT_MIN_HUBS_REG 0 +#define DEF_LIMIT_MIN_HUBS_OP 0 +#define DEF_LIMIT_MIN_SHARE 0 +#define DEF_LIMIT_MAX_SHARE 0 +#define DEF_LIMIT_MIN_SLOTS 0 +#define DEF_LIMIT_MAX_SLOTS 0 +#define DEF_MSG_HUB_FULL "Hub is full" +#define DEF_MSG_HUB_DISABLED "Hub is disabled" +#define DEF_MSG_HUB_REGISTERED_USERS_ONLY "Hub is for registered users only" +#define DEF_MSG_INF_ERROR_NICK_MISSING "No nickname given" +#define DEF_MSG_INF_ERROR_NICK_MULTIPLE "Multiple nicknames given" +#define DEF_MSG_INF_ERROR_NICK_INVALID "Nickname is invalid" +#define DEF_MSG_INF_ERROR_NICK_LONG "Nickname too long" +#define DEF_MSG_INF_ERROR_NICK_SHORT "Nickname too short" +#define DEF_MSG_INF_ERROR_NICK_SPACES "Nickname cannot start with spaces" +#define DEF_MSG_INF_ERROR_NICK_BAD_CHARS "Nickname contains invalid characters" +#define DEF_MSG_INF_ERROR_NICK_NOT_UTF8 "Nickname is not valid utf8" +#define DEF_MSG_INF_ERROR_NICK_TAKEN "Nickname is already in use" +#define DEF_MSG_INF_ERROR_NICK_RESTRICTED "Nickname cannot be used on this hub" +#define DEF_MSG_INF_ERROR_CID_INVALID "CID is not valid" +#define DEF_MSG_INF_ERROR_CID_MISSING "CID is not specified" +#define DEF_MSG_INF_ERROR_CID_TAKEN "CID is taken" +#define DEF_MSG_INF_ERROR_PID_MISSING "PID is not specified" +#define DEF_MSG_INF_ERROR_PID_INVALID "PID is invalid" +#define DEF_MSG_BAN_PERMANENTLY "Banned permanently" +#define DEF_MSG_BAN_TEMPORARILY "Banned temporarily" +#define DEF_MSG_AUTH_INVALID_PASSWORD "Password is wrong" +#define DEF_MSG_AUTH_USER_NOT_FOUND "User not found in password database" +#define DEF_MSG_ERROR_NO_MEMORY "No memory" +#define DEF_MSG_USER_SHARE_SIZE_LOW "User is not sharing enough" +#define DEF_MSG_USER_SHARE_SIZE_HIGH "User is sharing too much" +#define DEF_MSG_USER_SLOTS_LOW "User have too few upload slots." +#define DEF_MSG_USER_SLOTS_HIGH "User have too many upload slots." +#define DEF_MSG_USER_HUB_LIMIT_LOW "User is on too few hubs." +#define DEF_MSG_USER_HUB_LIMIT_HIGH "User is on too many hubs." + +void config_defaults(struct hub_config* config) +{ + DEFAULT_STRING (server_bind_addr, DEF_SERVER_BIND_ADDR); + DEFAULT_STRING (hub_name, DEF_HUB_NAME); + DEFAULT_STRING (hub_description, DEF_HUB_DESCRIPTION); + DEFAULT_BOOLEAN(hub_enabled, DEF_HUB_ENABLED); + DEFAULT_STRING (file_acl, DEF_FILE_ACL); + DEFAULT_STRING (file_motd, DEF_FILE_MOTD); + DEFAULT_INTEGER(server_port, DEF_SERVER_PORT); + DEFAULT_INTEGER(max_users, DEF_MAX_USERS); + DEFAULT_INTEGER(max_recv_buffer, DEF_MAX_RECV_BUFFER); + DEFAULT_INTEGER(max_send_buffer, DEF_MAX_SEND_BUFFER); + DEFAULT_INTEGER(max_send_buffer_soft, DEF_MAX_SEND_BUFFER_SOFT); + DEFAULT_BOOLEAN(show_banner, DEF_SHOW_BANNER); + DEFAULT_BOOLEAN(chat_only, DEF_CHAT_ONLY); + DEFAULT_BOOLEAN(chat_is_privileged, DEF_CHAT_IS_PRIVILEGED); + DEFAULT_BOOLEAN(low_bandwidth_mode, DEF_LOW_BANDWIDTH_MODE); + DEFAULT_BOOLEAN(registered_users_only, DEF_REGISTERED_USERS_ONLY); + + /* Limits enforced on users */ + DEFAULT_INTEGER(limit_max_hubs_user, DEF_LIMIT_MAX_HUBS_USER); + DEFAULT_INTEGER(limit_max_hubs_reg, DEF_LIMIT_MAX_HUBS_REG); + DEFAULT_INTEGER(limit_max_hubs_op, DEF_LIMIT_MAX_HUBS_OP); + DEFAULT_INTEGER(limit_min_hubs_user, DEF_LIMIT_MIN_HUBS_USER); + DEFAULT_INTEGER(limit_min_hubs_reg, DEF_LIMIT_MIN_HUBS_REG); + DEFAULT_INTEGER(limit_min_hubs_op, DEF_LIMIT_MIN_HUBS_OP); + DEFAULT_INTEGER(limit_max_hubs, DEF_LIMIT_MAX_HUBS); + DEFAULT_INTEGER(limit_min_share, DEF_LIMIT_MIN_SHARE); + DEFAULT_INTEGER(limit_max_share, DEF_LIMIT_MAX_SHARE); + DEFAULT_INTEGER(limit_min_slots, DEF_LIMIT_MIN_SLOTS); + DEFAULT_INTEGER(limit_max_slots, DEF_LIMIT_MAX_SLOTS); + + /* Status/error strings */ + DEFAULT_STRING (msg_hub_full, DEF_MSG_HUB_FULL); + DEFAULT_STRING (msg_hub_disabled, DEF_MSG_HUB_DISABLED) + DEFAULT_STRING (msg_hub_registered_users_only, DEF_MSG_HUB_REGISTERED_USERS_ONLY); + DEFAULT_STRING (msg_inf_error_nick_missing, DEF_MSG_INF_ERROR_NICK_MISSING); + DEFAULT_STRING (msg_inf_error_nick_multiple, DEF_MSG_INF_ERROR_NICK_MULTIPLE); + DEFAULT_STRING (msg_inf_error_nick_invalid, DEF_MSG_INF_ERROR_NICK_INVALID); + DEFAULT_STRING (msg_inf_error_nick_long, DEF_MSG_INF_ERROR_NICK_LONG); + DEFAULT_STRING (msg_inf_error_nick_short, DEF_MSG_INF_ERROR_NICK_SHORT); + DEFAULT_STRING (msg_inf_error_nick_spaces, DEF_MSG_INF_ERROR_NICK_SPACES); + DEFAULT_STRING (msg_inf_error_nick_bad_chars, DEF_MSG_INF_ERROR_NICK_BAD_CHARS); + DEFAULT_STRING (msg_inf_error_nick_not_utf8, DEF_MSG_INF_ERROR_NICK_NOT_UTF8); + DEFAULT_STRING (msg_inf_error_nick_taken, DEF_MSG_INF_ERROR_NICK_TAKEN); + DEFAULT_STRING (msg_inf_error_nick_restricted, DEF_MSG_INF_ERROR_NICK_RESTRICTED); + DEFAULT_STRING (msg_inf_error_cid_invalid, DEF_MSG_INF_ERROR_CID_INVALID); + DEFAULT_STRING (msg_inf_error_cid_missing, DEF_MSG_INF_ERROR_CID_MISSING); + DEFAULT_STRING (msg_inf_error_cid_taken, DEF_MSG_INF_ERROR_CID_TAKEN); + DEFAULT_STRING (msg_inf_error_pid_missing, DEF_MSG_INF_ERROR_PID_MISSING); + DEFAULT_STRING (msg_inf_error_pid_invalid, DEF_MSG_INF_ERROR_PID_INVALID); + DEFAULT_STRING (msg_ban_permanently, DEF_MSG_BAN_PERMANENTLY); + DEFAULT_STRING (msg_ban_temporarily, DEF_MSG_BAN_TEMPORARILY); + DEFAULT_STRING (msg_auth_invalid_password, DEF_MSG_AUTH_INVALID_PASSWORD); + DEFAULT_STRING (msg_auth_user_not_found, DEF_MSG_AUTH_USER_NOT_FOUND); + DEFAULT_STRING (msg_error_no_memory, DEF_MSG_ERROR_NO_MEMORY); + DEFAULT_STRING (msg_user_share_size_low, DEF_MSG_USER_SHARE_SIZE_LOW); + DEFAULT_STRING (msg_user_share_size_high, DEF_MSG_USER_SHARE_SIZE_HIGH); + DEFAULT_STRING (msg_user_slots_low, DEF_MSG_USER_SLOTS_LOW); + DEFAULT_STRING (msg_user_slots_high, DEF_MSG_USER_SLOTS_HIGH); + DEFAULT_STRING (msg_user_hub_limit_low, DEF_MSG_USER_HUB_LIMIT_LOW); + DEFAULT_STRING (msg_user_hub_limit_high, DEF_MSG_USER_HUB_LIMIT_HIGH); + + DEFAULT_INTEGER(tls_enable, 0); + DEFAULT_INTEGER(tls_require, 0); + DEFAULT_STRING (tls_certificate, ""); + DEFAULT_STRING (tls_private_key, ""); +} + + +static int apply_config(struct hub_config* config, char* key, char* data, int line_count) +{ + GET_STR (file_acl); + GET_STR (file_motd); + GET_STR (server_bind_addr); + GET_INT (server_port); + GET_STR (hub_name); + GET_STR (hub_description); + GET_BOOL(hub_enabled); + GET_INT (max_users); + GET_INT (max_recv_buffer); + GET_INT (max_send_buffer); + GET_INT (max_send_buffer_soft); + GET_BOOL(show_banner); + GET_BOOL(chat_only); + GET_BOOL(chat_is_privileged); + GET_BOOL(low_bandwidth_mode); + GET_BOOL(registered_users_only); + + /* Limits enforced on users */ + GET_INT(limit_max_hubs_user); + GET_INT(limit_max_hubs_reg); + GET_INT(limit_max_hubs_op); + GET_INT(limit_min_hubs_user); + GET_INT(limit_min_hubs_reg); + GET_INT(limit_min_hubs_op); + GET_INT(limit_max_hubs); + GET_INT(limit_min_share); + GET_INT(limit_max_share); + GET_INT(limit_min_slots); + GET_INT(limit_max_slots); + + /* Status/error strings */ + GET_STR (msg_hub_full); + GET_STR (msg_hub_disabled); + GET_STR (msg_hub_registered_users_only); + GET_STR (msg_inf_error_nick_missing); + GET_STR (msg_inf_error_nick_multiple); + GET_STR (msg_inf_error_nick_invalid); + GET_STR (msg_inf_error_nick_long); + GET_STR (msg_inf_error_nick_short); + GET_STR (msg_inf_error_nick_spaces); + GET_STR (msg_inf_error_nick_bad_chars); + GET_STR (msg_inf_error_nick_not_utf8); + GET_STR (msg_inf_error_nick_taken); + GET_STR (msg_inf_error_nick_restricted); + GET_STR (msg_inf_error_cid_invalid); + GET_STR (msg_inf_error_cid_missing); + GET_STR (msg_inf_error_cid_taken); + GET_STR (msg_inf_error_pid_missing); + GET_STR (msg_inf_error_pid_invalid); + GET_STR (msg_ban_permanently); + GET_STR (msg_ban_temporarily); + GET_STR (msg_auth_invalid_password); + GET_STR (msg_auth_user_not_found); + GET_STR (msg_error_no_memory); + GET_STR (msg_user_share_size_low); + GET_STR (msg_user_share_size_high); + GET_STR (msg_user_slots_low); + GET_STR (msg_user_slots_high); + GET_STR (msg_user_hub_limit_low); + GET_STR (msg_user_hub_limit_high); + + GET_BOOL(tls_enable); + GET_BOOL(tls_require); + GET_STR (tls_certificate); + GET_STR (tls_private_key); + + /* Still here -- unknown directive */ + hub_log(log_fatal, "Unknown configuration directive: '%s'", key); + return -1; +} + + +void free_config(struct hub_config* config) +{ + hub_free(config->server_bind_addr); + hub_free(config->file_motd); + hub_free(config->file_acl); + hub_free(config->hub_name); + hub_free(config->hub_description); + + hub_free(config->msg_hub_full); + hub_free(config->msg_hub_disabled); + hub_free(config->msg_hub_registered_users_only); + hub_free(config->msg_inf_error_nick_missing); + hub_free(config->msg_inf_error_nick_multiple); + hub_free(config->msg_inf_error_nick_invalid); + hub_free(config->msg_inf_error_nick_long); + hub_free(config->msg_inf_error_nick_short); + hub_free(config->msg_inf_error_nick_spaces); + hub_free(config->msg_inf_error_nick_bad_chars); + hub_free(config->msg_inf_error_nick_not_utf8); + hub_free(config->msg_inf_error_nick_taken); + hub_free(config->msg_inf_error_nick_restricted); + hub_free(config->msg_inf_error_cid_invalid); + hub_free(config->msg_inf_error_cid_missing); + hub_free(config->msg_inf_error_cid_taken); + hub_free(config->msg_inf_error_pid_missing); + hub_free(config->msg_inf_error_pid_invalid); + hub_free(config->msg_ban_permanently); + hub_free(config->msg_ban_temporarily); + hub_free(config->msg_auth_invalid_password); + hub_free(config->msg_auth_user_not_found); + hub_free(config->msg_error_no_memory); + hub_free(config->msg_user_share_size_low); + hub_free(config->msg_user_share_size_high); + hub_free(config->msg_user_slots_low); + hub_free(config->msg_user_slots_high); + hub_free(config->msg_user_hub_limit_low); + hub_free(config->msg_user_hub_limit_high); + + hub_free(config->tls_certificate); + hub_free(config->tls_private_key); + + memset(config, 0, sizeof(struct hub_config)); +} + +#define DUMP_STR(NAME, DEFAULT) \ + if (ignore_defaults) \ + { \ + if (strcmp(config->NAME, DEFAULT) != 0) \ + fprintf(stdout, "%s = \"%s\"\n", #NAME , config->NAME); \ + } \ + else \ + fprintf(stdout, "%s = \"%s\"\n", #NAME , config->NAME); \ + +#define DUMP_INT(NAME, DEFAULT) \ + if (ignore_defaults) \ + { \ + if (config->NAME != DEFAULT) \ + fprintf(stdout, "%s = \"%d\"\n", #NAME , config->NAME); \ + } \ + else \ + fprintf(stdout, "%s = \"%d\"\n", #NAME , config->NAME); \ + + +#define DUMP_BOOL(NAME, DEFAULT) \ + if (ignore_defaults) \ + { \ + if (config->NAME != DEFAULT) \ + fprintf(stdout, "%s = \"%s\"\n", #NAME , (config->NAME ? "yes" : "no")); \ + } \ + else \ + fprintf(stdout, "%s = \"%s\"\n", #NAME , (config->NAME ? "yes" : "no")); + +void dump_config(struct hub_config* config, int ignore_defaults) +{ + DUMP_STR (file_acl, DEF_FILE_ACL); + DUMP_STR (file_motd, DEF_FILE_MOTD); + DUMP_STR (server_bind_addr, DEF_SERVER_BIND_ADDR); + DUMP_INT (server_port, DEF_SERVER_PORT); + DUMP_STR (hub_name, DEF_HUB_NAME); + DUMP_STR (hub_description, DEF_HUB_DESCRIPTION); + DUMP_BOOL(hub_enabled, DEF_HUB_ENABLED); + DUMP_INT (max_users, DEF_MAX_USERS); + DUMP_INT (max_recv_buffer, DEF_MAX_RECV_BUFFER); + DUMP_INT (max_send_buffer, DEF_MAX_SEND_BUFFER); + DUMP_INT (max_send_buffer_soft, DEF_MAX_SEND_BUFFER_SOFT); + DUMP_BOOL(show_banner, DEF_SHOW_BANNER); + DUMP_BOOL(chat_only, DEF_CHAT_ONLY); + DUMP_BOOL(chat_is_privileged, DEF_CHAT_IS_PRIVILEGED); + DUMP_BOOL(low_bandwidth_mode, DEF_LOW_BANDWIDTH_MODE); + DUMP_BOOL(registered_users_only, DEF_REGISTERED_USERS_ONLY); + + /* Limits enforced on users */ + DUMP_INT(limit_max_hubs_user, DEF_LIMIT_MAX_HUBS_USER); + DUMP_INT(limit_max_hubs_reg, DEF_LIMIT_MAX_HUBS_REG); + DUMP_INT(limit_max_hubs_op, DEF_LIMIT_MAX_HUBS_OP); + DUMP_INT(limit_min_hubs_user, DEF_LIMIT_MIN_HUBS_USER); + DUMP_INT(limit_min_hubs_reg, DEF_LIMIT_MIN_HUBS_REG); + DUMP_INT(limit_min_hubs_op, DEF_LIMIT_MIN_HUBS_OP); + DUMP_INT(limit_max_hubs, DEF_LIMIT_MAX_HUBS); + DUMP_INT(limit_min_share, DEF_LIMIT_MIN_SHARE); + DUMP_INT(limit_max_share, DEF_LIMIT_MAX_SHARE); + DUMP_INT(limit_min_slots, DEF_LIMIT_MIN_SLOTS); + DUMP_INT(limit_max_slots, DEF_LIMIT_MAX_SLOTS); + + /* Status/error strings */ + DUMP_STR (msg_hub_full, DEF_MSG_HUB_FULL); + DUMP_STR (msg_hub_disabled, DEF_MSG_HUB_DISABLED); + DUMP_STR (msg_hub_registered_users_only, DEF_MSG_HUB_REGISTERED_USERS_ONLY); + DUMP_STR (msg_inf_error_nick_missing, DEF_MSG_INF_ERROR_NICK_MISSING); + DUMP_STR (msg_inf_error_nick_multiple, DEF_MSG_INF_ERROR_NICK_MULTIPLE); + DUMP_STR (msg_inf_error_nick_invalid, DEF_MSG_INF_ERROR_NICK_INVALID); + DUMP_STR (msg_inf_error_nick_long, DEF_MSG_INF_ERROR_NICK_LONG); + DUMP_STR (msg_inf_error_nick_short, DEF_MSG_INF_ERROR_NICK_SHORT); + DUMP_STR (msg_inf_error_nick_spaces, DEF_MSG_INF_ERROR_NICK_SPACES); + DUMP_STR (msg_inf_error_nick_bad_chars, DEF_MSG_INF_ERROR_NICK_BAD_CHARS); + DUMP_STR (msg_inf_error_nick_not_utf8, DEF_MSG_INF_ERROR_NICK_NOT_UTF8); + DUMP_STR (msg_inf_error_nick_taken, DEF_MSG_INF_ERROR_NICK_TAKEN); + DUMP_STR (msg_inf_error_nick_restricted, DEF_MSG_INF_ERROR_NICK_RESTRICTED); + DUMP_STR (msg_inf_error_cid_invalid, DEF_MSG_INF_ERROR_CID_INVALID); + DUMP_STR (msg_inf_error_cid_missing, DEF_MSG_INF_ERROR_CID_MISSING); + DUMP_STR (msg_inf_error_cid_taken, DEF_MSG_INF_ERROR_CID_TAKEN); + DUMP_STR (msg_inf_error_pid_missing, DEF_MSG_INF_ERROR_PID_MISSING); + DUMP_STR (msg_inf_error_pid_invalid, DEF_MSG_INF_ERROR_PID_INVALID); + DUMP_STR (msg_ban_permanently, DEF_MSG_BAN_PERMANENTLY); + DUMP_STR (msg_ban_temporarily, DEF_MSG_BAN_TEMPORARILY); + DUMP_STR (msg_auth_invalid_password, DEF_MSG_AUTH_INVALID_PASSWORD); + DUMP_STR (msg_auth_user_not_found, DEF_MSG_AUTH_USER_NOT_FOUND); + DUMP_STR (msg_error_no_memory, DEF_MSG_ERROR_NO_MEMORY); + DUMP_STR (msg_user_share_size_low, DEF_MSG_USER_SHARE_SIZE_LOW); + DUMP_STR (msg_user_share_size_high, DEF_MSG_USER_SHARE_SIZE_HIGH); + DUMP_STR (msg_user_slots_low, DEF_MSG_USER_SLOTS_LOW); + DUMP_STR (msg_user_slots_high, DEF_MSG_USER_SLOTS_HIGH); + DUMP_STR (msg_user_hub_limit_low, DEF_MSG_USER_HUB_LIMIT_LOW); + DUMP_STR (msg_user_hub_limit_high, DEF_MSG_USER_HUB_LIMIT_HIGH); +} + + +static int config_parse_line(char* line, int line_count, void* ptr_data) +{ + char* pos; + char* key; + char* data; + struct hub_config* config = (struct hub_config*) ptr_data; + + if ((pos = strchr(line, '#')) != NULL) + { + pos[0] = 0; + } + + if (strlen(line) == 0) return 0; + +#ifdef CONFIG_DUMP + hub_log(log_trace, "config_parse_line(): '%s'", line); +#endif + + if ((pos = strchr(line, '=')) != NULL) + { + pos[0] = 0; + } + else + { + return 0; + } + + key = line; + data = &pos[1]; + + key = strip_white_space(key); + data = strip_white_space(data); + + if (!strlen(key) || !strlen(data)) + { + hub_log(log_fatal, "Configuration parse error on line %d", line_count); + return -1; + } + +#ifdef CONFIG_DUMP + hub_log(log_trace, "config_parse_line: '%s' => '%s'", key, data); +#endif + + return apply_config(config, key, data, line_count); +} + + +int read_config(const char* file, struct hub_config* config, int allow_missing) +{ + int ret; + + memset(config, 0, sizeof(struct hub_config)); + + ret = file_read_lines(file, config, &config_parse_line); + if (ret < 0) + { + if (allow_missing && ret == -2) + { + hub_log(log_debug, "Using default configuration."); + } + else + { + return -1; + } + } + + config_defaults(config); + return 0; +} + + diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..1414711 --- /dev/null +++ b/src/config.h @@ -0,0 +1,122 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#ifndef HAVE_UHUB_CONFIG_H +#define HAVE_UHUB_CONFIG_H + +struct hub_config +{ + int server_port; /**<<< "Server port to bind to (default: 1511)" */ + char* server_bind_addr; /**<<< "Server bind address (default: '0.0.0.0' or '::')" */ + int hub_enabled; /**<<< "Is server enabled (default: 1)" */ + int show_banner; /**<<< "Show banner on connect (default: 1)" */ + int max_users; /**<<< "Maximum number of users allowed on the hub (default: 500)" */ + int registered_users_only; /**<<< "Allow registered users only (default: 0)" */ + int chat_only; /**<<< "Allow chat only operation on hub (default: 0)" */ + int chat_is_privileged; /**<<< "Allow chat for operators and above only (default: 0) */ + char* file_motd; /**<<< "File containing the 'message of the day' (default: '' - no motd)" */ + char* file_acl; /**<<< "File containing user database (default: '' - no known users)" */ + char* hub_name; /**<<< "Name of hub (default: 'My uhub hub')" */ + char* hub_description; /**<<< "Name of hub (default: 'no description')" */ + int max_recv_buffer; /**<<< "Max read buffer before parse, per user (default: 4096)" */ + int max_send_buffer; /**<<< "Max send buffer before disconnect, per user (default: 16384)" */ + int max_send_buffer_soft; /**<<< "Max send buffer before message drops, per user (default: 8192)" */ + int low_bandwidth_mode; /**<<< "If this is enabled, the hub will strip off elements from each user's info message to reduce bandwidth usage" */ + + /* Limits enforced on users */ + int limit_max_hubs_user; /**<<< "Max concurrent hubs as a user. (0=off, default: 10)" */ + int limit_max_hubs_reg; /**<<< "Max concurrent hubs as registered user. (0=off, default: 10)" */ + int limit_max_hubs_op; /**<<< "Max concurrent hubs as operator. (0=off, default: 10)" */ + int limit_min_hubs_user; /**<<< "Min concurrent hubs as a user. (0=off, default: 0)" */ + int limit_min_hubs_reg; /**<<< "Min concurrent hubs as registered user. (0=off, default: 0)" */ + int limit_min_hubs_op; /**<<< "Min concurrent hubs as operator. (0=off, default: 0)" */ + int limit_max_hubs; /**<<< "Max total hub connections allowed, user/reg/op combined. (0=off, default: 25)" */ + int limit_min_share; /**<<< "Limit minimum share size in megabytes (MiB) (0=off, default: 0)" */ + int limit_max_share; /**<<< "Limit maximum share size in megabytes (MiB) (0=off, default: 0)" */ + int limit_min_slots; /**<<< "Limit minimum number of slots open per user (0=off, default: 0)" */ + int limit_max_slots; /**<<< "Limit maximum number of slots open per user (0=off, default: 0)" */ + + /* Messages that can be sent to a user */ + char* msg_hub_full; /**<<< "hub is full" */ + char* msg_hub_disabled; /**<<< "hub is disabled" */ + char* msg_hub_registered_users_only; /**<<< "hub is for registered users only" */ + char* msg_inf_error_nick_missing; /**<<< "no nickname given" */ + char* msg_inf_error_nick_multiple; /**<<< "multiple nicknames given" */ + char* msg_inf_error_nick_invalid; /**<<< "generic/unkown" */ + char* msg_inf_error_nick_long; /**<<< "nickname too long" */ + char* msg_inf_error_nick_short; /**<<< "nickname too short" */ + char* msg_inf_error_nick_spaces; /**<<< "nickname cannot start with spaces" */ + char* msg_inf_error_nick_bad_chars; /**<<< "nickname contains chars below ascii 32" */ + char* msg_inf_error_nick_not_utf8; /**<<< "nickname is not valid utf8" */ + char* msg_inf_error_nick_taken; /**<<< "nickname is in use" */ + char* msg_inf_error_nick_restricted; /**<<< "nickname cannot be used on this hub" */ + char* msg_inf_error_cid_invalid; /**<<< "CID is not valid" */ + char* msg_inf_error_cid_missing; /**<<< "CID is not specified" */ + char* msg_inf_error_cid_taken; /**<<< "CID is taken" */ + char* msg_inf_error_pid_missing; /**<<< "PID is not specified" */ + char* msg_inf_error_pid_invalid; /**<<< "PID is invalid" */ + char* msg_ban_permanently; /**<<< "Banned permanently" */ + char* msg_ban_temporarily; /**<<< "Banned temporarily" */ + char* msg_auth_invalid_password; /**<<< "Password is wrong" */ + char* msg_auth_user_not_found; /**<<< "User not found in password database" */ + char* msg_error_no_memory; /**<<< "No memory" */ + char* msg_user_share_size_low; /**<<< "User is not sharing enough" */ + char* msg_user_share_size_high; /**<<< "User is sharing too much" */ + char* msg_user_slots_low; /**<<< "User have too few upload slots." */ + char* msg_user_slots_high; /**<<< "User have too many upload slots." */ + char* msg_user_hub_limit_low; /**<<< "User is on too few hubs." */ + char* msg_user_hub_limit_high; /**<<< "User is on too many hubs." */ + + int tls_enable; /**<<< "Enable SSL/TLS support (default: 0)" */ + int tls_require; /**<<< "If SSL/TLS enabled, should it be required (default: 0) */ + char* tls_certificate; /**<<< "Certificate file (PEM)" */ + char* tls_private_key; /**<<< "Private key" */ +}; + +/** + * This initializes the configuration variables, and sets the default + * variables. + * + * NOTE: Any variable is set to it's default variable if zero. + * This function is automatically called in read_config to set any + * configuration that was missing there. + */ +extern void config_defaults(struct hub_config* config); + +/** + * Read configuration from file, and use the default variables for + * the missing variables. + * + * @return -1 on error, 0 on success. + */ +extern int read_config(const char* file, struct hub_config* config, int allow_missing); + +/** + * Free the configuration data (allocated by read_config, or config_defaults). + */ +extern void free_config(struct hub_config* config); + +/** + * Print all configuration data to standard out. + */ +extern void dump_config(struct hub_config* config, int ignore_defaults); + + +#endif /* HAVE_UHUB_CONFIG_H */ + diff --git a/src/eventid.h b/src/eventid.h new file mode 100644 index 0000000..277ade2 --- /dev/null +++ b/src/eventid.h @@ -0,0 +1,37 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#ifndef HAVE_UHUB_EVENT_ID_H +#define HAVE_UHUB_EVENT_ID_H + +/* User join or quit messages */ +#define UHUB_EVENT_USER_JOIN 0x1001 +#define UHUB_EVENT_USER_QUIT 0x1002 +#define UHUB_EVENT_USER_DESTROY 0x1003 + +/* Send a broadcast message */ +#define UHUB_EVENT_BROADCAST 0x2000 + +/* Statistics, OOM, reconfigure */ +#define UHUB_EVENT_STATISTICS 0x4000 +#define UHUB_EVENT_OUT_OF_MEMORY 0x4001 +#define UHUB_EVENT_RECONFIGURE 0x4002 + +#endif /* HAVE_UHUB_EVENT_ID_H */ + diff --git a/src/eventqueue.c b/src/eventqueue.c new file mode 100644 index 0000000..3af8ce8 --- /dev/null +++ b/src/eventqueue.c @@ -0,0 +1,172 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#include "uhub.h" + +#ifdef EQ_DEBUG +static void eq_debug(const char* prefix, struct event_data* data) +{ + printf(">>> %s: %p, id: %x, flags=%d\n", prefix, data, data->id, data->flags); +} +#endif + + +int event_queue_initialize(struct event_queue** queue, event_queue_callback callback, void* ptr) +{ + *queue = (struct event_queue*) hub_malloc_zero(sizeof(struct event_queue)); + if (!(*queue)) + return -1; + + (*queue)->q1 = list_create(); + (*queue)->q2 = list_create(); + (*queue)->event = (struct event*) hub_malloc_zero(sizeof(struct event)); + + if (!(*queue)->q1 || !(*queue)->q2 || !(*queue)->event) + { + list_destroy((*queue)->q1); + list_destroy((*queue)->q2); + return -1; + } + + (*queue)->callback = callback; + (*queue)->callback_data = ptr; + + evtimer_set((*queue)->event, libevent_queue_process, *queue); + return 0; +} + + +void event_queue_shutdown(struct event_queue* queue) +{ + /* Should be empty at this point! */ + list_destroy(queue->q1); + list_destroy(queue->q2); + + if (queue->event) + { + evtimer_del(queue->event); + hub_free(queue->event); + } + hub_free(queue); +} + +static void event_queue_cleanup_callback(void* ptr) +{ +#ifdef EQ_DEBUG + struct event_data* data = (struct event_data*) ptr; + eq_debug("NUKE", data); +#endif + + hub_free((struct event_data*) ptr); +} + +int event_queue_process(struct event_queue* queue) +{ + struct event_data* data; + if (queue->locked) + return 0; + + /* lock primary queue, and handle the primary queue messages. */ + queue->locked = 1; + + data = (struct event_data*) list_get_first(queue->q1); + while (data) + { +#ifdef EQ_DEBUG + eq_debug("EXEC", data); +#endif + queue->callback(queue->callback_data, data); + data = (struct event_data*) list_get_next(queue->q1); + } + + list_clear(queue->q1, event_queue_cleanup_callback); + assert(list_size(queue->q1) == 0); + + /* unlock queue */ + queue->locked = 0; + + /* transfer from secondary queue to the primary queue. */ + data = (struct event_data*) list_get_first(queue->q2); + while (data) + { + list_remove(queue->q2, data); + list_append(queue->q1, data); + data = (struct event_data*) list_get_first(queue->q2); + } + + /* if more events exist, schedule it */ + if (list_size(queue->q1)) + { + return 1; + } + return 0; +} + +void event_queue_post(struct event_queue* queue, struct event_data* message) +{ + struct linked_list* q = (!queue->locked) ? queue->q1 : queue->q2; + struct event_data* data; + + data = (struct event_data*) hub_malloc(sizeof(struct event_data)); + if (data) + { + data->id = message->id; + data->ptr = message->ptr; + data->flags = message->flags; + +#ifdef EQ_DEBUG + eq_debug("POST", data); +#endif + + list_append(q, data); + + + if (!queue->locked && queue->event) + { + libevent_queue_schedule(queue); + } + } + else + { + hub_log(log_error, "event_queue_post: OUT OF MEMORY"); + } +} + + +size_t event_queue_size(struct event_queue* queue) +{ + return list_size(queue->q1) + list_size(queue->q2); +} + +void libevent_queue_schedule(struct event_queue* queue) +{ + struct timeval zero = { 0, }; + evtimer_add(queue->event, &zero); +} + +void libevent_queue_process(int fd, short events, void* arg) +{ + struct event_queue* queue = (struct event_queue*) arg; + if (event_queue_process(queue)) + { + libevent_queue_schedule(queue); + } +} + + diff --git a/src/eventqueue.h b/src/eventqueue.h new file mode 100644 index 0000000..417c66a --- /dev/null +++ b/src/eventqueue.h @@ -0,0 +1,55 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#ifndef HAVE_UHUB_EVENT_QUEUE_H +#define HAVE_UHUB_EVENT_QUEUE_H + +struct event_data +{ + int id; + void* ptr; + int flags; +}; + +typedef void (*event_queue_callback)(void* callback_data, struct event_data* event_data); + +struct event_queue +{ + int locked; + struct linked_list* q1; /* primary */ + struct linked_list* q2; /* secondary, when primary is locked */ + event_queue_callback callback; + void* callback_data; + struct event* event; /* libevent handle */ +}; + +extern int event_queue_initialize(struct event_queue** queue, event_queue_callback callback, void* ptr); +extern int event_queue_process(struct event_queue* queue); +extern void event_queue_shutdown(struct event_queue* queue); +extern void event_queue_post(struct event_queue* queue, struct event_data* message); +extern size_t event_queue_size(struct event_queue* queue); + +/** + * Only used internally with libevent. + */ +extern void libevent_queue_process(int fd, short events, void* arg); +extern void libevent_queue_schedule(struct event_queue* queue); + +#endif /* HAVE_UHUB_EVENT_QUEUE_H */ + diff --git a/src/hub.c b/src/hub.c new file mode 100644 index 0000000..7c2b07b --- /dev/null +++ b/src/hub.c @@ -0,0 +1,950 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#include "uhub.h" + +int hub_handle_message(struct user* u, const char* line, size_t length) +{ + int ret = 0; + struct adc_message* cmd = 0; + +#ifdef NETWORK_DUMP_DEBUG + hub_log(log_protocol, "recv %s: %s", sid_to_string(u->id.sid), line); +#endif + + if (user_is_disconnecting(u)) + return -1; + + cmd = adc_msg_parse_verify(u, line, length); + if (cmd) + { + switch (cmd->cmd) + { + case ADC_CMD_HSUP: ret = hub_handle_support(u, cmd); break; + case ADC_CMD_HPAS: ret = hub_handle_password(u, cmd); break; + case ADC_CMD_BINF: ret = hub_handle_info(u, cmd); break; + +#ifdef ADC_UDP_OPERATION + case ADC_CMD_HCHK: ret = hub_handle_autocheck(u, cmd); break; +#endif + case ADC_CMD_DINF: + case ADC_CMD_EINF: + case ADC_CMD_FINF: + /* these must never be allowed for security reasons, + so we ignore them. */ + break; + + case ADC_CMD_BSCH: + case ADC_CMD_DSCH: + case ADC_CMD_ESCH: + case ADC_CMD_FSCH: + case ADC_CMD_DRES: + case ADC_CMD_DRCM: + case ADC_CMD_DCTM: + if (u->hub->config->chat_only && u->credentials < cred_operator) + { + /* These below aren't allowed in chat only hubs */ + break; + } + + case ADC_CMD_EMSG: + case ADC_CMD_DMSG: + case ADC_CMD_BMSG: + case ADC_CMD_FMSG: + ret = hub_handle_chat_message(u, cmd); break; + + default: + if (user_is_logged_in(u)) + { + ret = route_message(u, cmd); + } + else + { + ret = -1; + } + break; + } + adc_msg_free(cmd); + } + else + { + if (!user_is_logged_in(u)) + { + ret = -1; + } + } + + return ret; +} + + +int hub_handle_support(struct user* u, struct adc_message* cmd) +{ + int ret = 0; + int index = 0; + int ok = 1; + char* arg = adc_msg_get_argument(cmd, index); + struct timeval timeout = { TIMEOUT_HANDSHAKE, 0 }; + + if (u->hub->status == hub_status_disabled && u->state == state_protocol) + { + on_login_failure(u, status_msg_hub_disabled); + return -1; + } + + while (arg) + { + + if (strlen(arg) == 6) + { + fourcc_t fourcc = FOURCC(arg[2], arg[3], arg[4], arg[5]); + if (strncmp(arg, ADC_SUP_FLAG_ADD, 2) == 0) + { + user_support_add(u, fourcc); + } + else if (strncmp(arg, ADC_SUP_FLAG_REMOVE, 2) == 0) + { + user_support_remove(u, fourcc); + } + else + { + ok = 0; + } + } + else + { + ok = 0; + } + + index++; + hub_free(arg); + arg = adc_msg_get_argument(cmd, index); + } + + if (u->state == state_protocol) + { + if (index == 0) ok = 0; /* Need to support *SOMETHING*, at least BASE */ + + if (ok) + { + hub_send_handshake(u); + if (u->ev_read) + event_add(u->ev_read, &timeout); + } + else + { + /* disconnect user. Do not send crap during initial handshake! */ + user_disconnect(u, quit_logon_error); + ret = -1; + } + } + + return ret; +} + + +int hub_handle_password(struct user* u, struct adc_message* cmd) +{ + char* password = adc_msg_get_argument(cmd, 0); + int ret = 0; + + if (u->state == state_verify) + { + if (password_verify(u, password)) + { + on_login_success(u); + } + else + { + on_login_failure(u, status_msg_auth_invalid_password); + ret = -1; + } + } + + hub_free(password); + return ret; +} + + +int hub_handle_chat_message(struct user* u, struct adc_message* cmd) +{ + char* message = adc_msg_get_argument(cmd, 0); + int ret = 0; + int relay = 1; + + /* TODO: Check for hub-commands here. Set relay to 0 and the message will not be sent to other users. */ + if (message[0] == '!') + { + relay = command_dipatcher(u, message); + } + + if (relay && user_is_logged_in(u)) + { + /* adc_msg_remove_named_argument(cmd, "PM"); */ + ret = route_message(u, cmd); + } + + free(message); + return ret; +} + +int on_kick(struct user* u, struct adc_message* cmd) +{ + hub_log(log_error, "on_kick() not implemented"); + return -1; +} + +#ifdef ADC_UDP_OPERATION +int hub_handle_autocheck(struct user* u, struct adc_message* cmd) +{ + char* port_str = adc_msg_get_argument(cmd, 0); + char* token = adc_msg_get_argument(cmd, 1); + int port = 0; + + if (!port_str || !token || strlen(token) != 4) + { + hub_free(port_str); + hub_free(token); + return -1; + } + + port = uhub_atoi(port_str); + + if (port == 0 || port > 65535) + { + hub_free(port_str); + hub_free(token); + return -1; + } + + hub_send_autocheck(u, port, token); + + hub_free(port_str); + hub_free(token); + + return 0; +} +#endif + + +void hub_send_autocheck(struct user* u, uint16_t port, const char* token) +{ + +} + + +void hub_send_support(struct user* u) +{ + if (user_is_connecting(u) || user_is_logged_in(u)) + { + route_to_user(u, u->hub->command_support); + } +} + + +void hub_send_sid(struct user* u) +{ + struct adc_message* command; + if (user_is_connecting(u)) + { + command = adc_msg_construct(ADC_CMD_ISID, 10); + u->id.sid = user_manager_get_free_sid(u->hub); + adc_msg_add_argument(command, (const char*) sid_to_string(u->id.sid)); + route_to_user(u, command); + adc_msg_free(command); + } +} + + +void hub_send_ping(struct user* user) +{ + /* This will just send a newline, despite appearing to do more below. */ + struct adc_message* ping = adc_msg_construct(0, 0); + ping->cache[0] = '\n'; + ping->cache[1] = 0; + ping->length = 1; + ping->priority = 1; + route_to_user(user, ping); + adc_msg_free(ping); +} + + +void hub_send_hubinfo(struct user* u) +{ + struct adc_message* info = adc_msg_copy(u->hub->command_info); + int value = 0; + + if (user_flag_get(u, feature_ping)) + { +/* + FIXME: These are missing: + HH - Hub Host address ( DNS or IP ) + WS - Hub Website + NE - Hub Network + OW - Hub Owner name +*/ + adc_msg_add_named_argument(info, "UC", uhub_itoa(hub_get_user_count(u->hub))); + adc_msg_add_named_argument(info, "MC", uhub_itoa(hub_get_max_user_count(u->hub))); + adc_msg_add_named_argument(info, "SS", uhub_ulltoa(hub_get_shared_size(u->hub))); + adc_msg_add_named_argument(info, "SF", uhub_itoa(hub_get_shared_files(u->hub))); + + /* Maximum/minimum share size */ + value = hub_get_max_share(u->hub); + if (value) adc_msg_add_named_argument(info, "XS", uhub_itoa(value)); + value = hub_get_min_share(u->hub); + if (value) adc_msg_add_named_argument(info, "MS", uhub_itoa(value)); + + /* Maximum/minimum upload slots allowed per user */ + value = hub_get_max_slots(u->hub); + if (value) adc_msg_add_named_argument(info, "XL", uhub_itoa(value)); + value = hub_get_min_slots(u->hub); + if (value) adc_msg_add_named_argument(info, "ML", uhub_itoa(value)); + + /* guest users must be on min/max hubs */ + value = hub_get_max_hubs_user(u->hub); + if (value) adc_msg_add_named_argument(info, "XU", uhub_itoa(value)); + value = hub_get_min_hubs_user(u->hub); + if (value) adc_msg_add_named_argument(info, "MU", uhub_itoa(value)); + + /* registered users must be on min/max hubs */ + value = hub_get_max_hubs_reg(u->hub); + if (value) adc_msg_add_named_argument(info, "XR", uhub_itoa(value)); + value = hub_get_min_hubs_reg(u->hub); + if (value) adc_msg_add_named_argument(info, "MR", uhub_itoa(value)); + + /* operators must be on min/max hubs */ + value = hub_get_max_hubs_op(u->hub); + if (value) adc_msg_add_named_argument(info, "XO", uhub_itoa(value)); + value = hub_get_min_hubs_op(u->hub); + if (value) adc_msg_add_named_argument(info, "MO", uhub_itoa(value)); + + /* uptime in seconds */ + adc_msg_add_named_argument(info, "UP", uhub_itoa((int) difftime(time(0), u->hub->tm_started))); + } + + if (user_is_connecting(u) || user_is_logged_in(u)) + { + route_to_user(u, info); + } + adc_msg_free(info); + + /* Only send banner when connecting */ + if (u->hub->config->show_banner && user_is_connecting(u)) + { + route_to_user(u, u->hub->command_banner); + } + + +} + + +void hub_send_handshake(struct user* u) +{ + hub_send_support(u); + hub_send_sid(u); + hub_send_hubinfo(u); + + if (!user_is_disconnecting(u)) + { + user_set_state(u, state_identify); + } +} + + +void hub_send_motd(struct user* u) +{ + if (u->hub->command_motd) + { + route_to_user(u, u->hub->command_motd); + } +} + + +void hub_send_password_challenge(struct user* u) +{ + struct adc_message* igpa; + igpa = adc_msg_construct(ADC_CMD_IGPA, 38); + adc_msg_add_argument(igpa, password_generate_challenge(u)); + user_set_state(u, state_verify); + route_to_user(u, igpa); + adc_msg_free(igpa); +} + +static void hub_event_dispatcher(void* callback_data, struct event_data* message) +{ +/* + struct hub_info* hub = (struct hub_info*) callback_data; + hub_log(log_trace, "hub_event_dispatcher: %x (ptr=%p)", message->id, message->ptr); +*/ + + switch (message->id) + { + case UHUB_EVENT_USER_JOIN: + { + if (user_is_disconnecting((struct user*) message->ptr)) + break; + + if (message->flags) + { + hub_send_password_challenge((struct user*) message->ptr); + } + else + { + on_login_success((struct user*) message->ptr); + } + break; + } + + case UHUB_EVENT_USER_QUIT: + { + user_manager_remove((struct user*) message->ptr); + send_quit_message((struct user*) message->ptr); + on_logout_user((struct user*) message->ptr); + user_schedule_destroy((struct user*) message->ptr); + break; + } + + case UHUB_EVENT_USER_DESTROY: + { + hub_log(log_trace, "hub_event_dispatcher: UHUB_EVENT_USER_DESTROY (ptr=%p)", message->ptr); + user_destroy((struct user*) message->ptr); + break; + } + + default: + /* FIXME: ignored */ + break; + } +} + + +struct hub_info* hub_start_service(struct hub_config* config) +{ + struct hub_info* hub = 0; + int server_tcp, ret, ipv6_supported, af; +#ifdef ADC_UDP_OPERATION + int server_udp; +#endif + struct sockaddr_storage addr; + socklen_t sockaddr_size; + char address_buf[INET6_ADDRSTRLEN+1]; + + hub = hub_malloc_zero(sizeof(struct hub_info)); + if (!hub) + { + hub_log(log_fatal, "Unable to allocate memory for hub"); + return 0; + } + + hub->tm_started = time(0); + + ipv6_supported = net_is_ipv6_supported(); + + if (ipv6_supported) + hub_log(log_debug, "IPv6 supported."); + else + hub_log(log_debug, "IPv6 not supported."); + + if (ip_convert_address(config->server_bind_addr, config->server_port, (struct sockaddr*) &addr, &sockaddr_size) == -1) + { + hub_free(hub); + return 0; + } + + af = addr.ss_family; + if (af == AF_INET) + { + net_address_to_string(AF_INET, &((struct sockaddr_in*) &addr)->sin_addr, address_buf, INET6_ADDRSTRLEN); + } + else if (af == AF_INET6) + { + net_address_to_string(AF_INET6, &((struct sockaddr_in6*) &addr)->sin6_addr, address_buf, INET6_ADDRSTRLEN); + } + + hub_log(log_info, "Starting server, listening on %s:%d...", address_buf, config->server_port); + + server_tcp = net_socket_create(af, SOCK_STREAM, IPPROTO_TCP); + if (server_tcp == -1) + { + hub_free(hub); + return 0; + } + +#ifdef ADC_UDP_OPERATION + server_udp = net_socket_create(af, SOCK_DGRAM, IPPROTO_UDP); + if (server_udp == -1) + { + hub_free(hub); + return 0; + } +#endif + + ret = net_set_reuseaddress(server_tcp, 1); + if (ret == -1) + { + hub_free(hub); + net_close(server_tcp); +#ifdef ADC_UDP_OPERATION + net_close(server_udp); +#endif + return 0; + } + +#ifdef ADC_UDP_OPERATION + ret = net_set_reuseaddress(server_udp, 1); + if (ret == -1) + { + hub_free(hub); + net_close(server_tcp); + net_close(server_udp); + return 0; + } +#endif + + + ret = net_set_nonblocking(server_tcp, 1); + if (ret == -1) + { + hub_free(hub); + net_close(server_tcp); +#ifdef ADC_UDP_OPERATION + net_close(server_udp); +#endif + return 0; + } + +#ifdef ADC_UDP_OPERATION + ret = net_set_nonblocking(server_udp, 1); + if (ret == -1) + { + hub_free(hub); + net_close(server_tcp); + net_close(server_udp); + return 0; + } +#endif + + + ret = net_bind(server_tcp, (struct sockaddr*) &addr, sockaddr_size); + if (ret == -1) + { + hub_log(log_fatal, "hub_start_service(): Unable to bind to TCP local address. errno=%d, str=%s", net_error(), net_error_string(net_error())); + hub_free(hub); + net_close(server_tcp); +#ifdef ADC_UDP_OPERATION + net_close(server_udp); +#endif + return 0; + } + +#ifdef ADC_UDP_OPERATION + ret = net_bind(server_udp, (struct sockaddr*) &addr, sockaddr_size); + if (ret == -1) + { + hub_log(log_fatal, "hub_start_service(): Unable to bind to UDP local address. errno=%d, str=%s", net_error(), net_error_string(net_error())); + hub_free(hub); + net_close(server_tcp); + net_close(server_udp); + return 0; + } +#endif + + ret = net_listen(server_tcp, SERVER_BACKLOG); + if (ret == -1) + { + hub_log(log_fatal, "hub_start_service(): Unable to listen to socket"); + hub_free(hub); + net_close(server_tcp); +#ifdef ADC_UDP_OPERATION + net_close(server_udp); +#endif + return 0; + } + + hub->fd_tcp = server_tcp; +#ifdef ADC_UDP_OPERATION + hub->fd_udp = server_udp; +#endif + hub->config = config; + hub->users = NULL; + + if (user_manager_init(hub) == -1) + { + hub_free(hub); + net_close(server_tcp); +#ifdef ADC_UDP_OPERATION + net_close(server_udp); +#endif + return 0; + } + + event_set(&hub->ev_accept, hub->fd_tcp, EV_READ | EV_PERSIST, net_on_accept, hub); + if (event_add(&hub->ev_accept, NULL) == -1) + { + user_manager_shutdown(hub); + hub_free(hub); + net_close(server_tcp); +#ifdef ADC_UDP_OPERATION + net_close(server_udp); +#endif + return 0; + } + +#ifdef ADC_UDP_OPERATION + event_set(&hub->ev_datagram, hub->fd_udp, EV_READ | EV_PERSIST, net_on_packet, hub); + if (event_add(&hub->ev_datagram, NULL) == -1) + { + user_manager_shutdown(hub); + hub_free(hub); + net_close(server_tcp); + net_close(server_udp); + return 0; + } +#endif + + if (event_queue_initialize(&hub->queue, hub_event_dispatcher, (void*) hub) == -1) + { + user_manager_shutdown(hub); + hub_free(hub); + net_close(server_tcp); +#ifdef ADC_UDP_OPERATION + net_close(server_udp); +#endif + return 0; + } + + hub->status = hub_status_running; + + return hub; +} + + +void hub_shutdown_service(struct hub_info* hub) +{ + hub_log(log_trace, "hub_shutdown_service()"); + + event_queue_shutdown(hub->queue); + event_del(&hub->ev_accept); +#ifdef ADC_UDP_OPERATION + event_del(&hub->ev_datagram); + net_close(hub->fd_udp); +#endif + net_close(hub->fd_tcp); + user_manager_shutdown(hub); + hub->status = hub_status_stopped; + hub_free(hub); + hub = 0; +} + +#define SERVER "" PRODUCT "/" VERSION "" + +void hub_set_variables(struct hub_info* hub, struct acl_handle* acl) +{ + int fd, ret; + char buf[MAX_RECV_BUF]; + char* tmp; + + + hub->acl = acl; + hub->command_info = adc_msg_construct(ADC_CMD_IINF, 15 + strlen(SERVER)); + if (hub->command_info) + { + adc_msg_add_named_argument(hub->command_info, ADC_INF_FLAG_CLIENT_TYPE, ADC_CLIENT_TYPE_HUB); + adc_msg_add_named_argument(hub->command_info, ADC_INF_FLAG_USER_AGENT, SERVER); + + tmp = adc_msg_escape(hub->config->hub_name); + adc_msg_add_named_argument(hub->command_info, ADC_INF_FLAG_NICK, tmp); + hub_free(tmp); + + tmp = adc_msg_escape(hub->config->hub_description); + adc_msg_add_named_argument(hub->command_info, ADC_INF_FLAG_DESCRIPTION, tmp); + hub_free(tmp); + } + + /* (Re-)read the message of the day */ + hub->command_motd = 0; + fd = open(hub->config->file_motd, 0); + if (fd != -1) + { + ret = read(fd, buf, MAX_RECV_BUF); + if (ret > 0) + { + buf[ret] = 0; + tmp = adc_msg_escape(buf); + hub->command_motd = adc_msg_construct(ADC_CMD_IMSG, 6 + strlen(tmp)); + adc_msg_add_argument(hub->command_motd, tmp); + hub_free(tmp); + } + else + { + + } + close(fd); + } + + hub->command_support = adc_msg_construct(ADC_CMD_ISUP, 6 + strlen(ADC_PROTO_SUPPORT)); + if (hub->command_support) + { + adc_msg_add_argument(hub->command_support, ADC_PROTO_SUPPORT); + } + + hub->command_banner = adc_msg_construct(ADC_CMD_ISTA, 25 + strlen(SERVER)); + if (hub->command_banner) + { + adc_msg_add_argument(hub->command_banner, "000 Powered\\sby\\s" SERVER); + } + + hub->status = (hub->config->hub_enabled ? hub_status_running : hub_status_disabled); +} + + +void hub_free_variables(struct hub_info* hub) +{ + adc_msg_free(hub->command_info); + adc_msg_free(hub->command_banner); + + if (hub->command_motd) + adc_msg_free(hub->command_motd); + + adc_msg_free(hub->command_support); +} + + +/** + * @return 1 if nickname is in use, or 0 if not used. + */ +static inline int is_nick_in_use(struct hub_info* hub, const char* nick) +{ + struct user* lookup = get_user_by_nick(hub, nick); + if (lookup) + { + return 1; + } + return 0; +} + + +/** + * @return 1 if CID is in use, or 0 if not used. + */ +static inline int is_cid_in_use(struct hub_info* hub, const char* cid) +{ + struct user* lookup = get_user_by_cid(hub, cid); + if (lookup) + { + return 1; + } + return 0; +} + + + + +static void set_status_code(enum msg_status_level level, int code, char buffer[4]) +{ + buffer[0] = ('0' + (int) level); + buffer[1] = ('0' + (code / 10)); + buffer[2] = ('0' + (code % 10)); + buffer[3] = 0; +} + +/** + * @param hub The hub instance this message is sent from. + * @param user The user this message is sent to. + * @param msg See enum status_message + * @param level See enum status_level + */ +void hub_send_status(struct user* user, enum status_message msg, enum msg_status_level level) +{ + struct hub_config* cfg = user->hub->config; + struct adc_message* cmd = adc_msg_construct(ADC_CMD_ISTA, 6); + if (!cmd) return; + char code[4]; + const char* text = 0; + const char* flag = 0; + char* escaped_text = 0; + +#define STATUS(CODE, MSG, FLAG) case status_ ## MSG : set_status_code(level, CODE, code); text = cfg->MSG; flag = FLAG; break + switch (msg) + { + STATUS(11, msg_hub_full, 0); + STATUS(12, msg_hub_disabled, 0); + STATUS(26, msg_hub_registered_users_only, 0); + STATUS(43, msg_inf_error_nick_missing, 0); + STATUS(43, msg_inf_error_nick_multiple, 0); + STATUS(21, msg_inf_error_nick_invalid, 0); + STATUS(21, msg_inf_error_nick_long, 0); + STATUS(21, msg_inf_error_nick_short, 0); + STATUS(21, msg_inf_error_nick_spaces, 0); + STATUS(21, msg_inf_error_nick_bad_chars, 0); + STATUS(21, msg_inf_error_nick_not_utf8, 0); + STATUS(22, msg_inf_error_nick_taken, 0); + STATUS(21, msg_inf_error_nick_restricted, 0); + STATUS(43, msg_inf_error_cid_invalid, "FBID"); + STATUS(43, msg_inf_error_cid_missing, "FMID"); + STATUS(24, msg_inf_error_cid_taken, 0); + STATUS(43, msg_inf_error_pid_missing, "FMPD"); + STATUS(27, msg_inf_error_pid_invalid, "FBPD"); + STATUS(31, msg_ban_permanently, 0); + STATUS(32, msg_ban_temporarily, "TL600"); /* FIXME: Use a proper timeout */ + STATUS(23, msg_auth_invalid_password, 0); + STATUS(20, msg_auth_user_not_found, 0); + STATUS(30, msg_error_no_memory, 0); + STATUS(43, msg_user_share_size_low, "FB" ADC_INF_FLAG_SHARED_SIZE); + STATUS(43, msg_user_share_size_high, "FB" ADC_INF_FLAG_SHARED_SIZE); + STATUS(43, msg_user_slots_low, "FB" ADC_INF_FLAG_UPLOAD_SLOTS); + STATUS(43, msg_user_slots_high, "FB" ADC_INF_FLAG_UPLOAD_SLOTS); + STATUS(43, msg_user_hub_limit_low, 0); + STATUS(43, msg_user_hub_limit_high, 0); + } +#undef STATUS + + escaped_text = adc_msg_escape(text); + + adc_msg_add_argument(cmd, code); + adc_msg_add_argument(cmd, escaped_text); + + hub_free(escaped_text); + + if (flag) + { + adc_msg_add_argument(cmd, flag); + } + + route_to_user(user, cmd); + adc_msg_free(cmd); + +} + +const char* hub_get_status_message(struct hub_info* hub, enum status_message msg) +{ +#define STATUS(MSG) case status_ ## MSG : return cfg->MSG; break + struct hub_config* cfg = hub->config; + switch (msg) + { + STATUS(msg_hub_full); + STATUS(msg_hub_disabled); + STATUS(msg_hub_registered_users_only); + STATUS(msg_inf_error_nick_missing); + STATUS(msg_inf_error_nick_multiple); + STATUS(msg_inf_error_nick_invalid); + STATUS(msg_inf_error_nick_long); + STATUS(msg_inf_error_nick_short); + STATUS(msg_inf_error_nick_spaces); + STATUS(msg_inf_error_nick_bad_chars); + STATUS(msg_inf_error_nick_not_utf8); + STATUS(msg_inf_error_nick_taken); + STATUS(msg_inf_error_nick_restricted); + STATUS(msg_inf_error_cid_invalid); + STATUS(msg_inf_error_cid_missing); + STATUS(msg_inf_error_cid_taken); + STATUS(msg_inf_error_pid_missing); + STATUS(msg_inf_error_pid_invalid); + STATUS(msg_ban_permanently); + STATUS(msg_ban_temporarily); + STATUS(msg_auth_invalid_password); + STATUS(msg_auth_user_not_found); + STATUS(msg_error_no_memory); + STATUS(msg_user_share_size_low); + STATUS(msg_user_share_size_high); + STATUS(msg_user_slots_low); + STATUS(msg_user_slots_high); + STATUS(msg_user_hub_limit_low); + STATUS(msg_user_hub_limit_high); + } +#undef STATUS + return "Unknown"; +} + +size_t hub_get_user_count(struct hub_info* hub) +{ + return hub->users->count; +} + +size_t hub_get_max_user_count(struct hub_info* hub) +{ + return hub->config->max_users; +} + +uint64_t hub_get_shared_size(struct hub_info* hub) +{ + return hub->users->shared_size; +} + +uint64_t hub_get_shared_files(struct hub_info* hub) +{ + return hub->users->shared_files; +} + +uint64_t hub_get_min_share(struct hub_info* hub) +{ + return 1024 * 1024 * hub->config->limit_min_share; +} + +uint64_t hub_get_max_share(struct hub_info* hub) +{ + return 1024 * 1024 * hub->config->limit_max_share; +} + +size_t hub_get_min_slots(struct hub_info* hub) +{ + return hub->config->limit_min_slots; +} + +size_t hub_get_max_slots(struct hub_info* hub) +{ + return hub->config->limit_max_slots; +} + +size_t hub_get_max_hubs_total(struct hub_info* hub) +{ + return hub->config->limit_max_hubs; +} + +size_t hub_get_max_hubs_user(struct hub_info* hub) +{ + return hub->config->limit_max_hubs_user; +} + +size_t hub_get_min_hubs_user(struct hub_info* hub) +{ + return hub->config->limit_min_hubs_user; +} + +size_t hub_get_max_hubs_reg(struct hub_info* hub) +{ + return hub->config->limit_max_hubs_reg; +} + +size_t hub_get_min_hubs_reg(struct hub_info* hub) +{ + return hub->config->limit_min_hubs_reg; +} + +size_t hub_get_max_hubs_op(struct hub_info* hub) +{ + return hub->config->limit_max_hubs_op; +} + +size_t hub_get_min_hubs_op(struct hub_info* hub) +{ + return hub->config->limit_min_hubs_op; +} + + diff --git a/src/hub.h b/src/hub.h new file mode 100644 index 0000000..319572c --- /dev/null +++ b/src/hub.h @@ -0,0 +1,326 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#ifndef HAVE_UHUB_HUB_H +#define HAVE_UHUB_HUB_H + +enum status_message +{ + status_msg_hub_full = -1, /* hub is full */ + status_msg_hub_disabled = -2, /* hub is disabled */ + status_msg_hub_registered_users_only = -3, /* hub is for registered users only */ + status_msg_inf_error_nick_missing = -4, /* no nickname given */ + status_msg_inf_error_nick_multiple = -5, /* multiple nicknames given */ + status_msg_inf_error_nick_invalid = -6, /* generic/unkown */ + status_msg_inf_error_nick_long = -7, /* nickname too long */ + status_msg_inf_error_nick_short = -8, /* nickname too short */ + status_msg_inf_error_nick_spaces = -9, /* nickname cannot start with spaces */ + status_msg_inf_error_nick_bad_chars = -10, /* nickname contains chars below ascii 32 */ + status_msg_inf_error_nick_not_utf8 = -11, /* nickname is not valid utf8 */ + status_msg_inf_error_nick_taken = -12, /* nickname is in use */ + status_msg_inf_error_nick_restricted = -13, /* nickname cannot be used on this hub */ + status_msg_inf_error_cid_invalid = -14, /* CID is not valid (generic error) */ + status_msg_inf_error_cid_missing = -15, /* CID is not specified */ + status_msg_inf_error_cid_taken = -16, /* CID is taken (already logged in?). */ + status_msg_inf_error_pid_missing = -17, /* PID is not specified */ + status_msg_inf_error_pid_invalid = -18, /* PID is invalid */ + status_msg_ban_permanently = -19, /* Banned permanently */ + status_msg_ban_temporarily = -20, /* Banned temporarily */ + status_msg_auth_invalid_password = -21, /* Password is wrong */ + status_msg_auth_user_not_found = -22, /* User not found in password database */ + status_msg_error_no_memory = -23, /* Hub is out of memory */ + + status_msg_user_share_size_low = -40, /* User is not sharing enough. */ + status_msg_user_share_size_high = -41, /* User is sharing too much. */ + status_msg_user_slots_low = -42, /* User has too few slots open. */ + status_msg_user_slots_high = -43, /* User has too many slots open. */ + status_msg_user_hub_limit_low = -44, /* Use is on too few hubs. */ + status_msg_user_hub_limit_high = -45, /* Use is on too many hubs. */ + +}; + + +enum hub_state +{ + hub_status_uninitialized = 0, /**<<<"Hub is uninitialized" */ + hub_status_running = 1, /**<<<"Hub is running (normal operation)" */ + hub_status_restart = 2, /**<<<"Hub is restarting (re-reading configuration, etc)" */ + hub_status_shutdown = 3, /**<<<"Hub is shutting down, but not yet stopped. */ + hub_status_stopped = 4, /**<<<"Hub is stopped (Pretty much the same as initialized) */ + hub_status_disabled = 5, /**<<<"Hub is disabled (Running, but not accepting users) */ +}; + +struct hub_info +{ + int fd_tcp; +#ifdef ADC_UDP_OPERATION + int fd_udp; +#endif + struct event ev_accept; + struct event ev_timer; +#ifdef ADC_UDP_OPERATION + struct event ev_datagram; +#endif + struct event_queue* queue; + struct hub_config* config; + struct user_manager* users; + struct acl_handle* acl; + struct adc_message* command_info; /* The hub's INF command */ + struct adc_message* command_support; /* The hub's SUP command */ + struct adc_message* command_motd; /* The message of the day */ + struct adc_message* command_banner; /* The default welcome message */ + time_t tm_started; + int status; +#ifdef SSL_SUPPORT + SSL_METHOD* ssl_method; + SSL_CTX* ssl_ctx; +#endif /* SSL_SUPPORT */ +}; + +/** + * This is the message pre-routing centre. + * + * Any message coming in to the hub comes through here first, + * and will be routed further if valid. + * + * @return 0 on success, -1 on error + */ +extern int hub_handle_message(struct user* u, const char* message, size_t length); + +/** + * Handle protocol support/subscription messages received clients. + * + * @return 0 on success, -1 on error + */ +extern int hub_handle_support(struct user* u, struct adc_message* cmd); + +/** + * Handle password messages received from clients. + * + * @return 0 on success, -1 on error + */ +extern int hub_handle_password(struct user* u, struct adc_message* cmd); + +/** + * Handle chat messages received from clients. + * @return 0 on success, -1 on error. + */ +extern int hub_handle_chat_message(struct user* u, struct adc_message* cmd); + +/** + * Used internally by hub_handle_info + * @return 1 if nickname is OK, or 0 if nickname is not accepted. + */ +extern int hub_handle_info_check_nick(struct user* u, struct adc_message* cmd); + +/** + * Used internally by hub_handle_info + * @return 1 if CID/PID is OK, or 0 if not valid. + */ +extern int hub_handle_info_check_cid(struct user* u, struct adc_message* cmd); + +/** + * Can only be used by administrators or operators. + * + * @return 0 on success, -1 on error + */ +extern int hub_handle_kick(struct user* u, struct adc_message* cmd); + +#ifdef ADC_UDP_OPERATION +/** + * Handle incoming autocheck message. + */ +extern int hub_handle_autocheck(struct user* u, struct adc_message* cmd); +#endif + +/** + * Send the support line for the hub to a particular user. + * Only used during the initial handshake. + */ +extern void hub_send_support(struct user* u); + +/** + * Send a message assigning a SID for a user. + * This is only sent after hub_send_support() during initial handshake. + */ +extern void hub_send_sid(struct user* u); + +/** + * Send a 'ping' message to user. + */ +extern void hub_send_ping(struct user* user); + +/** + * Send a message containing hub information to a particular user. + * This is sent during user connection, but can safely be sent at any + * point later. + */ +extern void hub_send_hubinfo(struct user* u); + +/** + * Send handshake. This basically calls + * hub_send_support() and hub_send_sid() + */ +extern void hub_send_handshake(struct user* u); + +/** + * Send a welcome message containing the message of the day to + * one particular user. This can be sent in any point in time. + */ +extern void hub_send_motd(struct user* u); + +/** + * Send a password challenge to a user. + * This is only used if the user tries to access the hub using a + * password protected nick name. + */ +extern void hub_send_password_challenge(struct user* u); + +/** + * Send an autocheck message to a user. + * This is basically a UDP message. The user's client can then determine + * if UDP communication works by either hole punching or configuring UPnP. + */ +extern void hub_send_autocheck(struct user* u, uint16_t port, const char* token); + +/** + * This starts the hub. + */ +extern struct hub_info* hub_start_service(struct hub_config* config); + +/** + * This shuts down the hub. + */ +extern void hub_shutdown_service(struct hub_info* hub); + +/** + * This configures the hub. + */ +extern void hub_set_variables(struct hub_info* hub, struct acl_handle* acl); + +/** + * This frees the configuration of the hub. + */ +extern void hub_free_variables(struct hub_info* hub); + +/** + * Returns a string for the given status_message (See enum status_message). + */ +extern const char* hub_get_status_message(struct hub_info* hub, enum status_message msg); + +/** + * Sends a status_message to a user. + */ +extern void hub_send_status(struct user* user, enum status_message msg, enum msg_status_level level); + +/** + * Returns the number of logged in users on the hub. + */ +extern size_t hub_get_user_count(struct hub_info* hub); + +/** + * Returns the maximum number of allowed users on the hub. + */ +extern size_t hub_get_max_user_count(struct hub_info* hub); + +/** + * Returns the accumulated shared size for all logged in + * users on the hub. + */ +extern uint64_t hub_get_shared_size(struct hub_info* hub); + +/** + * Returns the accumulated number of files for all logged + * in users on the hub. + */ +extern uint64_t hub_get_shared_files(struct hub_info* hub); + +/** + * Returns the minimal share size limit as enforced by + * this hub's configuration. + */ +extern uint64_t hub_get_min_share(struct hub_info* hub); + +/** + * Returns the minimal share size limit as enforced by + * this hub's configuration. + */ +extern uint64_t hub_get_max_share(struct hub_info* hub); + +/** + * Returns the minimum upload slot limit as enforced by + * this hub's configuration. + * Users with fewer slots in total will not be allowed + * to enter the hub. + * @return limit or 0 if no limit. + */ +extern size_t hub_get_min_slots(struct hub_info* hub); + +/** + * Returns the maximum upload slot limit as enforced by + * this hub's configuration. + * Users with more allowed upload slots will not be + * allowed to enter the hub. + * @return limit or 0 if no limit. + */ +extern size_t hub_get_max_slots(struct hub_info* hub); + +/** + * Returns the maximum number of hubs a user can + * be logged in to simultaneously as a regular user (guest). + * Users on more hubs will not be allowed to stay on this hub. + * @return limit or 0 if no limit. + */ +extern size_t hub_get_max_hubs_user(struct hub_info* hub); +extern size_t hub_get_min_hubs_user(struct hub_info* hub); + +/** + * Returns the maximum number of hubs a user can + * be logged in to simultaneously as a registered user (password required). + * Users on more hubs will not be allowed to stay on this hub. + * @return limit or 0 if no limit. + */ +extern size_t hub_get_max_hubs_reg(struct hub_info* hub); +extern size_t hub_get_min_hubs_reg(struct hub_info* hub); + +/** + * Returns the maximum number of hubs a user can + * be logged in to simultaneously as an operator. + * Users who are operator on more than this amount of hubs + * will not be allowed to stay on this hub. + * @return limit or 0 if no limit. + */ +extern size_t hub_get_max_hubs_op(struct hub_info* hub); +extern size_t hub_get_min_hubs_op(struct hub_info* hub); + +/** + * Returns the maximum number of hubs a user can + * be logged in to simultaneously regardless of the type of user. + */ +extern size_t hub_get_max_hubs_total(struct hub_info* hub); + +/** + * Schedule runslice. + */ +extern void hub_schedule_runslice(struct hub_info* hub); + + + +#endif /* HAVE_UHUB_HUB_H */ + diff --git a/src/hubevent.c b/src/hubevent.c new file mode 100644 index 0000000..d9ae6fa --- /dev/null +++ b/src/hubevent.c @@ -0,0 +1,107 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#include "uhub.h" + +/* Send MOTD, do logging etc */ +void on_login_success(struct user* u) +{ + /* Logging - FIXME: Move this to a plugin */ + const char* addr = net_get_peer_address(u->sd); + const char* credentials_string[] = { "!none!", "link", "guest", "user", "operator", "super", "admin" }; + struct timeval timeout = { TIMEOUT_IDLE, 0 }; + + /* Send user list of all existing users */ + if (!send_user_list(u)) + return; + + /* Mark as being in the normal state, and add user to the user list */ + user_set_state(u, state_normal); + user_manager_add(u); + + /* Print log message */ + hub_log(log_user, "Login OK %s/%s \"%s\" [%s] (%s) \"%s\"", sid_to_string(u->id.sid), u->id.cid, u->id.nick, addr, credentials_string[u->credentials], u->user_agent); + + /* Announce new user to all connected users */ + if (user_is_logged_in(u)) + route_info_message(u); + + /* Send message of the day (if any) */ + if (user_is_logged_in(u)) /* Previous send() can fail! */ + hub_send_motd(u); + + /* reset to idle timeout */ + if (u->ev_read) + event_add(u->ev_read, &timeout); +} + + +void on_login_failure(struct user* u, enum status_message msg) +{ + const char* addr = net_get_peer_address(u->sd); + const char* message = hub_get_status_message(u->hub, msg); + hub_log(log_user, "Login FAIL %s/%s \"%s\" [%s] (%s) \"%s\"", sid_to_string(u->id.sid), u->id.cid, u->id.nick, addr, message, u->user_agent); + + hub_send_status(u, msg, status_level_fatal); + user_disconnect(u, quit_logon_error); +} + + +void on_nick_change(struct user* u, const char* nick) +{ + if (user_is_logged_in(u)) + { + hub_log(log_user, "Nick change %s/%s \"%s\" -> \"%s\"", sid_to_string(u->id.sid), u->id.cid, u->id.nick, nick); + } +} + + +void on_logout_user(struct user* user) +{ + const char* reason = ""; + const char* addr; + + /* These are used for logging purposes */ + switch (user->quit_reason) + { + case quit_disconnected: reason = "disconnected"; break; + case quit_kicked: reason = "kicked"; break; + case quit_banned: reason = "banned"; break; + case quit_timeout: reason = "timeout"; break; + case quit_send_queue: reason = "send queue"; break; + case quit_memory_error: reason = "out of memory"; break; + case quit_socket_error: reason = "socket error"; break; + case quit_protocol_error: reason = "protocol error"; break; + case quit_logon_error: reason = "login error"; break; + case quit_hub_disabled: reason = "hub disabled"; break; + default: + if (user->hub->status == hub_status_shutdown) + reason = "hub shutdown"; + else + reason = "unknown error"; + break; + } + + addr = ip_convert_to_string(&user->ipaddr); + hub_log(log_user, "Logout %s/%s \"%s\" [%s] (%s)", sid_to_string(user->id.sid), user->id.cid, user->id.nick, addr, reason); + + + user->quit_reason = 0; +} + diff --git a/src/hubevent.h b/src/hubevent.h new file mode 100644 index 0000000..746c935 --- /dev/null +++ b/src/hubevent.h @@ -0,0 +1,45 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#ifndef HAVE_UHUB_HUB_EVENT_H +#define HAVE_UHUB_HUB_EVENT_H + +/** + * This event is triggered whenever a user successfully logs in to the hub. + */ +extern void on_login_success(struct user* u); + +/** + * This event is triggered whenever a user failed to log in to the hub. + */ +extern void on_login_failure(struct user* u, enum status_message msg); + +/** + * This event is triggered whenever a previously logged in user leaves the hub. + */ +extern void on_logout_user(struct user* u); + +/** + * This event is triggered whenever a user changes his/her nickname. + */ +extern void on_nick_change(struct user* u, const char* nick); + + +#endif /* HAVE_UHUB_HUB_EVENT_H */ + diff --git a/src/inf.c b/src/inf.c new file mode 100644 index 0000000..23ae829 --- /dev/null +++ b/src/inf.c @@ -0,0 +1,859 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#include "uhub.h" + +/* + * These flags can only be set by the hub. + * Make sure we don't allow clients to specify these themselves. + * + * NOTE: Some of them are legacy ADC flags and no longer used, these + * should be removed at some point in the future when functionality no + * longer depend on them. + */ +static void remove_server_restricted_flags(struct adc_message* cmd) +{ + adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_CLIENT_TYPE); /* Client type flag (CT, obsoletes BO, RG, OP, HU) */ + adc_msg_remove_named_argument(cmd, "BO"); /* Obsolete: bot flag (CT) */ + adc_msg_remove_named_argument(cmd, "RG"); /* Obsolete: registered user flag (CT) */ + adc_msg_remove_named_argument(cmd, "OP"); /* Obsolete: operator flag (CT) */ + adc_msg_remove_named_argument(cmd, "HU"); /* Obsolete: hub flag (CT) */ + adc_msg_remove_named_argument(cmd, "HI"); /* Obsolete: hidden user flag */ + adc_msg_remove_named_argument(cmd, "TO"); /* Client to client token - should not be seen here */ + adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_REFERER); +} + +static int user_is_protected(struct user* user); + +static int set_feature_cast_supports(struct user* u, struct adc_message* cmd) +{ + char *it, *tmp; + + if (adc_msg_has_named_argument(cmd, ADC_INF_FLAG_SUPPORT)) + { + tmp = adc_msg_get_named_argument(cmd, ADC_INF_FLAG_SUPPORT); + user_clear_feature_cast_support(u); + + it = tmp; + while (strlen(it) > 4) + { + it[4] = 0; /* FIXME: Not really needed */ + user_set_feature_cast_support(u, it); + it = &it[5]; + } + + if (strlen(it) > 0) + { + user_set_feature_cast_support(u, it); + } + hub_free(tmp); + } + return 0; +} + + +static int check_hash_tiger(const char* cid, const char* pid) +{ + char x_pid[64]; + char raw_pid[64]; + uint64_t tiger_res[3]; + + memset(x_pid, 0, MAX_CID_LEN+1); + + base32_decode(pid, (unsigned char*) raw_pid, MAX_CID_LEN); + tiger((uint64_t*) raw_pid, TIGERSIZE, (uint64_t*) tiger_res); + base32_encode((unsigned char*) tiger_res, TIGERSIZE, x_pid); + x_pid[MAX_CID_LEN] = 0; + if (strncasecmp(x_pid, cid, MAX_CID_LEN) == 0) + return 1; + return 0; +} + + +/* + * FIXME: Only works for tiger hash. If a client doesnt support tiger we cannot let it in! + */ +static int check_cid(struct user* user, struct adc_message* cmd) +{ + size_t pos; + char* cid = adc_msg_get_named_argument(cmd, ADC_INF_FLAG_CLIENT_ID); + char* pid = adc_msg_get_named_argument(cmd, ADC_INF_FLAG_PRIVATE_ID); + + if (!cid || !pid) + { + hub_free(cid); + hub_free(pid); + return status_msg_error_no_memory; + } + + if (strlen(cid) != MAX_CID_LEN) + { + hub_free(cid); + hub_free(pid); + return status_msg_inf_error_cid_invalid; + } + + if (strlen(pid) != MAX_CID_LEN) + { + hub_free(cid); + hub_free(pid); + return status_msg_inf_error_pid_invalid; + } + + for (pos = 0; pos < MAX_CID_LEN; pos++) + { + if (!is_valid_base32_char(cid[pos])) + { + hub_free(cid); + hub_free(pid); + return status_msg_inf_error_cid_invalid; + } + + if (!is_valid_base32_char(pid[pos])) + { + hub_free(cid); + hub_free(pid); + return status_msg_inf_error_pid_invalid; + } + } + + if (!check_hash_tiger(cid, pid)) + { + hub_free(cid); + hub_free(pid); + return status_msg_inf_error_cid_invalid; + } + + /* Set the cid in the user object */ + memcpy(user->id.cid, cid, MAX_CID_LEN); + user->id.cid[MAX_CID_LEN] = 0; + + hub_free(cid); + hub_free(pid); + return 0; +} + + +static int check_required_login_flags(struct user* user, struct adc_message* cmd) +{ + int num = 0; + + num = adc_msg_has_named_argument(cmd, ADC_INF_FLAG_CLIENT_ID); + if (num != 1) + { + if (!num) + return status_msg_inf_error_cid_missing; + return status_msg_inf_error_cid_invalid; + } + + num = adc_msg_has_named_argument(cmd, ADC_INF_FLAG_PRIVATE_ID); + if (num != 1) + { + if (!num) + return status_msg_inf_error_pid_missing; + return status_msg_inf_error_pid_invalid; + } + + num = adc_msg_has_named_argument(cmd, ADC_INF_FLAG_NICK); + if (num != 1) + { + if (!num) + return status_msg_inf_error_nick_missing; + return status_msg_inf_error_nick_multiple; + } + return 0; +} + + +/** + * This will check the ip address of the user, and + * remove any wrong address, and replace it with the correct one + * as seen by the hub. + */ +int check_network(struct user* user, struct adc_message* cmd) +{ + int want_ipv4 = 0; + int want_ipv6 = 0; + int nat_override = 0; + const char* address = 0; + + if (adc_msg_has_named_argument(cmd, ADC_INF_FLAG_IPV6_ADDR)) + { + want_ipv6 = 1; + } + + if (adc_msg_has_named_argument(cmd, ADC_INF_FLAG_IPV4_ADDR)) + { + want_ipv4 = 1; + } + + if (!want_ipv4 && !want_ipv6) + return 0; + + /* Add correct/verified IP addresses instead (if requested/stripped) */ + address = (char*) net_get_peer_address(user->sd); + if (address) + { + if (want_ipv4 && strchr(address, '.')) + { + want_ipv6 = 0; + } + else if (want_ipv6) + { + want_ipv4 = 0; + } + + /* check if user can do nat override */ + if (want_ipv4 && acl_is_ip_nat_override(user->hub->acl, address)) + { + char* client_given_ip = adc_msg_get_named_argument(cmd, ADC_INF_FLAG_IPV4_ADDR); + if (strcmp(client_given_ip, "0.0.0.0") != 0) + { + user_set_nat_override(user); + nat_override = 1; + } + hub_free(client_given_ip); + } + } + + if (!nat_override) + { + adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV4_ADDR); + if (!want_ipv4) + adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV4_UDP_PORT); + else + adc_msg_add_named_argument(cmd, ADC_INF_FLAG_IPV4_ADDR, address); + + adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV6_ADDR); + if (!want_ipv6) + adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV6_UDP_PORT); + else + adc_msg_add_named_argument(cmd, ADC_INF_FLAG_IPV6_ADDR, address); + } + + return 0; +} + + +static int nick_length_ok(const char* nick) +{ + size_t length = strlen(nick); + if (length <= 1) + { + return nick_invalid_short; + } + + if (length > MAX_NICK_LEN) + { + return nick_invalid_long; + } + + return nick_ok; +} + + +static int nick_bad_characters(const char* nick) +{ + const char* tmp; + + /* Nick must not start with a space */ + if (nick[0] == ' ') + return nick_invalid_spaces; + + /* Check for ASCII values below 32 */ + for (tmp = nick; *tmp; tmp++) + if ((*tmp < 32) && (*tmp > 0)) + return nick_invalid_bad_ascii; + + return nick_ok; +} + + +static int nick_is_utf8(const char* nick) +{ + /* + * Nick should be valid utf-8, but + * perhaps we should check if the nick is unicode normalized? + */ + if (!is_valid_utf8(nick)) + return nick_invalid_bad_utf8; + return nick_ok; +} + + +static int check_nick(struct user* user, struct adc_message* cmd) +{ + char* nick; + char* tmp; + enum nick_status status; + + tmp = adc_msg_get_named_argument(cmd, ADC_INF_FLAG_NICK); + if (!tmp) return 0; + nick = adc_msg_unescape(tmp); + free(tmp); tmp = 0; + if (!nick) return 0; + + status = nick_length_ok(nick); + if (status != nick_ok) + { + hub_free(nick); + if (status == nick_invalid_short) + return status_msg_inf_error_nick_short; + return status_msg_inf_error_nick_long; + } + + status = nick_bad_characters(nick); + if (status != nick_ok) + { + hub_free(nick); + if (status == nick_invalid_spaces) + return status_msg_inf_error_nick_spaces; + return status_msg_inf_error_nick_bad_chars; + } + + status = nick_is_utf8(nick); + if (status != nick_ok) + { + hub_free(nick); + return status_msg_inf_error_nick_not_utf8; + } + + if (user_is_connecting(user)) + { + memcpy(user->id.nick, nick, strlen(nick)); + user->id.nick[strlen(nick)] = 0; + } + + hub_free(nick); + return 0; +} + + +static int check_logged_in(struct user* user, struct adc_message* cmd) +{ + struct user* lookup1 = get_user_by_nick(user->hub, user->id.nick); + struct user* lookup2 = get_user_by_cid(user->hub, user->id.cid); + + if (lookup1 == user) + { + return 0; + } + + if (lookup1 || lookup2) + { + if (lookup1 == lookup2) + { + hub_log(log_debug, "check_logged_in: exact same user is logged in: %s", user->id.nick); + user_disconnect(lookup1, quit_timeout); + return 0; + } + else + { + if (lookup1) + { + hub_log(log_debug, "check_logged_in: nickname is in use: %s", user->id.nick); + return status_msg_inf_error_nick_taken; + } + else + { + hub_log(log_debug, "check_logged_in: CID is in use: %s", user->id.cid); + return status_msg_inf_error_cid_taken; + } + } + } + return 0; +} + + +/* + * It is possible to do user-agent checking here. + * But this is not something we want to do, and is deprecated in the ADC specification. + * One should rather look at capabilities/features. + */ +static int check_user_agent(struct user* user, struct adc_message* cmd) +{ + char* ua_encoded = 0; + char* ua = 0; + + /* Get client user agent version */ + ua_encoded = adc_msg_get_named_argument(cmd, ADC_INF_FLAG_USER_AGENT); + if (ua_encoded) + { + ua = adc_msg_unescape(ua_encoded); + if (ua) + { + memcpy(user->user_agent, ua, MIN(strlen(ua), MAX_UA_LEN)); + hub_free(ua); + } + } + hub_free(ua_encoded); + return 0; +} + + +static int check_acl(struct user* user, struct adc_message* cmd) +{ + if (acl_is_cid_banned(user->hub->acl, user->id.cid)) + { + return status_msg_ban_permanently; + } + + if (acl_is_user_banned(user->hub->acl, user->id.nick)) + { + return status_msg_ban_permanently; + } + + if (acl_is_user_denied(user->hub->acl, user->id.nick)) + { + return status_msg_inf_error_nick_restricted; + } + + return 0; +} + +static int check_limits(struct user* user, struct adc_message* cmd) +{ + char* arg = adc_msg_get_named_argument(cmd, ADC_INF_FLAG_SHARED_SIZE); + if (arg) + { + int64_t shared_size = atoll(arg); + if (shared_size < 0) + shared_size = 0; + + if (user_is_logged_in(user)) + { + user->hub->users->shared_size -= user->limits.shared_size; + user->hub->users->shared_size += shared_size; + } + user->limits.shared_size = shared_size; + hub_free(arg); + arg = 0; + } + + arg = adc_msg_get_named_argument(cmd, ADC_INF_FLAG_SHARED_FILES); + if (arg) + { + ssize_t shared_files = atoll(arg); + if (shared_files < 0) + shared_files = 0; + + if (user_is_logged_in(user)) + { + user->hub->users->shared_files -= user->limits.shared_files; + user->hub->users->shared_files += shared_files; + } + user->limits.shared_files = shared_files; + hub_free(arg); + arg = 0; + } + + arg = adc_msg_get_named_argument(cmd, ADC_INF_FLAG_COUNT_HUB_NORMAL); + if (arg) + { + ssize_t num = atoll(arg); + if (num < 0) num = 0; + user->limits.hub_count_user = num; + hub_free(arg); + arg = 0; + } + + arg = adc_msg_get_named_argument(cmd, ADC_INF_FLAG_COUNT_HUB_REGISTER); + if (arg) + { + ssize_t num = atoll(arg); + if (num < 0) num = 0; + user->limits.hub_count_registered = num; + hub_free(arg); + arg = 0; + } + + arg = adc_msg_get_named_argument(cmd, ADC_INF_FLAG_COUNT_HUB_OPERATOR); + if (arg) + { + ssize_t num = atoll(arg); + if (num < 0) num = 0; + user->limits.hub_count_operator = num; + hub_free(arg); + arg = 0; + } + + arg = adc_msg_get_named_argument(cmd, ADC_INF_FLAG_UPLOAD_SLOTS); + if (arg) + { + ssize_t num = atoll(arg); + if (num < 0) num = 0; + user->limits.upload_slots = num; + hub_free(arg); + arg = 0; + } + + /* summarize total slots */ + user->limits.hub_count_total = user->limits.hub_count_user + user->limits.hub_count_registered + user->limits.hub_count_operator; + + if (!user_is_protected(user)) + { + if (user->limits.shared_size < hub_get_min_share(user->hub) && hub_get_min_share(user->hub)) + { + return status_msg_user_share_size_low; + } + + if (user->limits.shared_size > hub_get_max_share(user->hub) && hub_get_max_share(user->hub)) + { + return status_msg_user_share_size_high; + } + + if ((user->limits.hub_count_user > hub_get_max_hubs_user(user->hub) && hub_get_max_hubs_user(user->hub)) || + (user->limits.hub_count_registered > hub_get_max_hubs_reg(user->hub) && hub_get_max_hubs_reg(user->hub)) || + (user->limits.hub_count_operator > hub_get_max_hubs_op(user->hub) && hub_get_max_hubs_op(user->hub)) || + (user->limits.hub_count_total > hub_get_max_hubs_total(user->hub) && hub_get_max_hubs_total(user->hub))) + { + return status_msg_user_hub_limit_high; + } + + if ((user->limits.hub_count_user < hub_get_min_hubs_user(user->hub) && hub_get_min_hubs_user(user->hub)) || + (user->limits.hub_count_registered < hub_get_min_hubs_reg(user->hub) && hub_get_min_hubs_reg(user->hub)) || + (user->limits.hub_count_operator < hub_get_min_hubs_op(user->hub) && hub_get_min_hubs_op(user->hub))) + { + return status_msg_user_hub_limit_low; + } + + if (user->limits.upload_slots < hub_get_min_slots(user->hub) && hub_get_min_slots(user->hub)) + { + return status_msg_user_slots_low; + } + + if (user->limits.upload_slots > hub_get_max_slots(user->hub) && hub_get_max_slots(user->hub)) + { + return status_msg_user_slots_high; + } + } + return 0; +} + + +/* + * Set the expected credentials, and returns 1 if authentication is needed, + * or 0 if not. + * If the hub is configured to allow only registered users and the user + * is not recognized this will return 1. + */ +static int set_credentials(struct user* user, struct adc_message* cmd) +{ + int ret = 0; + struct user_access_info* info = acl_get_access_info(user->hub->acl, user->id.nick); + + if (info) + { + user->credentials = info->status; + ret = 1; + } + else + { + user->credentials = cred_guest; + } + + switch (user->credentials) + { + case cred_none: + break; + + case cred_bot: + adc_msg_add_argument(cmd, ADC_INF_FLAG_CLIENT_TYPE ADC_CLIENT_TYPE_BOT); + break; + + case cred_guest: + /* Nothing to be added to the info message */ + break; + + case cred_user: + adc_msg_add_argument(cmd, ADC_INF_FLAG_CLIENT_TYPE ADC_CLIENT_TYPE_REGISTERED_USER); + break; + + case cred_operator: + adc_msg_add_argument(cmd, ADC_INF_FLAG_CLIENT_TYPE ADC_CLIENT_TYPE_OPERATOR); + break; + + case cred_super: + adc_msg_add_argument(cmd, ADC_INF_FLAG_CLIENT_TYPE ADC_CLIENT_TYPE_SUPER_USER); + break; + + case cred_admin: + adc_msg_add_argument(cmd, ADC_INF_FLAG_CLIENT_TYPE ADC_CLIENT_TYPE_ADMIN); + break; + + case cred_link: + break; + } + + return ret; +} + + +/** + * Determines if a user is to be let into the hub even if the hub is "full". + */ +static int user_is_protected(struct user* user) +{ + switch (user->credentials) + { + case cred_bot: + case cred_operator: + case cred_super: + case cred_admin: + case cred_link: + return 1; + default: + break; + } + return 0; +} + +/** + * Returns 1 if a user is registered. + * Only registered users will be let in if the hub is configured for registered + * users only. + */ +static int user_is_registered(struct user* user) +{ + switch (user->credentials) + { + case cred_bot: + case cred_user: + case cred_operator: + case cred_super: + case cred_admin: + case cred_link: + return 1; + default: + break; + } + return 0; +} + + +void update_user_info(struct user* u, struct adc_message* cmd) +{ + char prefix[2]; + char* argument; + size_t n = 0; + struct adc_message* cmd_new = adc_msg_copy(u->info); + if (!cmd_new) + { + /* FIXME: OOM! */ + return; + } + + argument = adc_msg_get_argument(cmd, n++); + while (argument) + { + if (strlen(argument) >= 2) + { + prefix[0] = argument[0]; + prefix[1] = argument[1]; + adc_msg_replace_named_argument(cmd_new, prefix, argument+2); + } + + hub_free(argument); + argument = adc_msg_get_argument(cmd, n++); + } + user_set_info(u, cmd_new); +} + + +static int check_is_hub_full(struct user* user) +{ + /* + * If hub is full, don't let users in, but we still want to allow + * operators and admins to enter the hub. + */ + if (user->hub->config->max_users && user->hub->users->count >= user->hub->config->max_users && !user_is_protected(user)) + { + return 1; + } + return 0; +} + + +static int check_registered_users_only(struct user* user) +{ + if (user->hub->config->registered_users_only && !user_is_registered(user)) + { + return 1; + } + return 0; +} + +#define INF_CHECK(FUNC, USER, CMD) \ + do { \ + int ret = FUNC(USER, CMD); \ + if (ret < 0) \ + return ret; \ + } while(0) + +static int hub_handle_info_common(struct user* user, struct adc_message* cmd) +{ + /* Remove server restricted flags */ + remove_server_restricted_flags(cmd); + + /* Update/set the feature cast flags. */ + set_feature_cast_supports(user, cmd); + + return 0; +} + +static int hub_handle_info_low_bandwidth(struct user* user, struct adc_message* cmd) +{ + if (user->hub->config->low_bandwidth_mode) + { + adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_USER_AGENT); + adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_SHARED_FILES); + adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_COUNT_HUB_NORMAL); + adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_COUNT_HUB_REGISTER); + adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_COUNT_HUB_OPERATOR); + adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_UPLOAD_SPEED); + adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_DOWNLOAD_SPEED); + adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_AUTO_SLOTS); + adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_AUTO_SLOTS_MAX); + adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_AWAY); + adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_DESCRIPTION); + adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_EMAIL); + } + + return 0; +} + +int hub_handle_info_login(struct user* user, struct adc_message* cmd) +{ + int need_auth = 0; + + /* Make syntax checks. */ + INF_CHECK(check_required_login_flags, user, cmd); + INF_CHECK(check_cid, user, cmd); + INF_CHECK(check_nick, user, cmd); + INF_CHECK(check_network, user, cmd); + INF_CHECK(check_user_agent, user, cmd); + INF_CHECK(check_acl, user, cmd); + INF_CHECK(check_logged_in, user, cmd); + + /* Private ID must never be broadcasted - drop it! */ + adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_PRIVATE_ID); + + /* FIXME: This needs some cleaning up */ + need_auth = set_credentials(user, cmd); + + /* Note: this must be done *after* set_credentials. */ + if (check_is_hub_full(user)) + { + return status_msg_hub_full; + } + + if (check_registered_users_only(user)) + { + return status_msg_hub_registered_users_only; + } + + INF_CHECK(check_limits, user, cmd); + + /* strip off stuff if low_bandwidth_mode is enabled */ + hub_handle_info_low_bandwidth(user, cmd); + + /* Set initial user info */ + user_set_info(user, cmd); + + return need_auth; +} + +/* + * If user is in the connecting state, we need to do fairly + * strict checking of all arguments. + * This means we disconnect users when they provide invalid data + * during the login sequence. + * When users are merely updating their data after successful login + * we can just ignore any invalid data and not broadcast it. + * + * The data we need to check is: + * - nick name (valid, not taken, etc) + * - CID/PID (valid, not taken, etc). + * - IP addresses (IPv4 and IPv6) + */ +int hub_handle_info(struct user* user, const struct adc_message* cmd_unmodified) +{ + struct adc_message* cmd = adc_msg_copy(cmd_unmodified); /* FIXME: Have a small memory leak here! */ + if (!cmd) return -1; /* OOM */ + + hub_handle_info_common(user, cmd); + + /* If user is logging in, perform more checks, + otherwise only a few things need to be checked. + */ + if (user_is_connecting(user)) + { + int ret = hub_handle_info_login(user, cmd); + if (ret < 0) + { + on_login_failure(user, ret); + adc_msg_free(cmd); + return -1; + } + else + { + /* Post a message, the user has joined */ + struct event_data post; + memset(&post, 0, sizeof(post)); + post.id = UHUB_EVENT_USER_JOIN; + post.ptr = user; + post.flags = ret; /* 0 - all OK, 1 - need authentication */ + event_queue_post(user->hub->queue, &post); + adc_msg_free(cmd); + return 0; + } + } + else + { + /* These must not be allowed updated, let's remove them! */ + adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_PRIVATE_ID); + adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_CLIENT_ID); + + /* + * If the nick is not accepted, do not relay it. + * Otherwise, the nickname will be updated. + */ + if (adc_msg_has_named_argument(cmd, ADC_INF_FLAG_NICK)) + { +#if ALLOW_CHANGE_NICK + if (!check_nick(user, cmd)) +#endif + adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_NICK); + } + + /* FIXME - What if limits are not met ? */ + check_limits(user, cmd); + hub_handle_info_low_bandwidth(user, cmd); + update_user_info(user, cmd); + + if (!adc_msg_is_empty(cmd)) + { + route_message(user, cmd); + } + + adc_msg_free(cmd); + } + + return 0; +} diff --git a/src/inf.h b/src/inf.h new file mode 100644 index 0000000..e0b611d --- /dev/null +++ b/src/inf.h @@ -0,0 +1,54 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#ifndef HAVE_UHUB_INF_PARSER_H +#define HAVE_UHUB_INF_PARSER_H + +enum nick_status +{ + nick_ok = 0, + nick_invalid_short = -1, + nick_invalid_long = -2, + nick_invalid_spaces = -3, + nick_invalid_bad_ascii = -4, + nick_invalid_bad_utf8 = -5, + nick_invalid = -6, /* some unknown reason */ + nick_not_allowed = -7, /* Not allowed according to configuration */ + nick_banned = -8, /* Nickname is banned */ +}; + +/** + * Handle info messages as received from clients. + * This can be an initial info message, which might end up requiring password + * authentication, etc. + * All sorts of validation is performed here. + * - Nickname valid? + * - CID/PID valid? + * - Network IP address valid? + * + * This can be triggered multiple times, as users can update their information, + * in such case nickname and CID/PID changes are not allowed. + * + * @return 0 on success, -1 on error + */ +extern int hub_handle_info(struct user* u, const struct adc_message* cmd); + + +#endif /* HAVE_UHUB_INF_PARSER_H */ + diff --git a/src/ipcalc.c b/src/ipcalc.c new file mode 100644 index 0000000..f3070f0 --- /dev/null +++ b/src/ipcalc.c @@ -0,0 +1,425 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#include "uhub.h" + + +int ip_is_valid_ipv4(const char* address) +{ + int i = 0; /* address index */ + int o = 0; /* octet number */ + int n = 0; /* numbers after each dot */ + int d = 0; /* dots */ + + if (!address || strlen(address) > 15 || strlen(address) < 7) + return 0; + + for (; i < strlen(address); i++) + { + if (is_num(address[i])) + { + n++; + o *= 10; + o += (address[i] - '0'); + } + else if (address[i] == '.') + { + if (n == 0 || n > 3 || o > 255) return 0; + n = 0; + o = 0; + d++; + } + else + { + return 0; + } + } + + if (n == 0 || n > 3 || o > 255 || d != 3) return 0; + + return 1; +} + + +int ip_is_valid_ipv6(const char* address) +{ + unsigned char buf[16]; + int ret = net_string_to_address(AF_INET6, address, buf); + if (ret <= 0) return 0; + return 1; +} + + +int ip_convert_to_binary(const char* taddr, struct ip_addr_encap* raw) +{ + if (ip_is_valid_ipv6(taddr)) + { + if (net_string_to_address(AF_INET6, taddr, &raw->internal_ip_data.in6) <= 0) + { + return -1; + } + raw->af = AF_INET6; + return AF_INET6; + } + else if (ip_is_valid_ipv4(taddr)) + { + if (net_string_to_address(AF_INET, taddr, &raw->internal_ip_data.in) <= 0) + { + return -1; + } + raw->af = AF_INET; + return AF_INET; + } + return -1; +} + + +char* ip_convert_to_string(struct ip_addr_encap* raw) +{ + static char address[INET6_ADDRSTRLEN+1]; + memset(address, 0, INET6_ADDRSTRLEN); + net_address_to_string(raw->af, (void*) &raw->internal_ip_data, address, INET6_ADDRSTRLEN+1); + if (strncmp(address, "::ffff:", 7) == 0) /* IPv6 mapped IPv4 address. */ + { + return &address[7]; + } + return address; +} + +int ip_convert_address(const char* text_address, int port, struct sockaddr* addr, socklen_t* addr_len) +{ + struct sockaddr_in6 addr6; + struct sockaddr_in addr4; + size_t sockaddr_size; + const char* taddr = 0; + + int ipv6sup = net_is_ipv6_supported(); + + if (strcmp(text_address, "any") == 0) + { + if (ipv6sup) + { + taddr = "::"; + } + else + { + taddr = "0.0.0.0"; + } + } + else if (strcmp(text_address, "loopback") == 0) + { + if (ipv6sup) + { + taddr = "::1"; + } + else + { + taddr = "127.0.0.1"; + } + } + else + { + taddr = text_address; + } + + + if (ip_is_valid_ipv6(taddr) && ipv6sup) + { + sockaddr_size = sizeof(struct sockaddr_in6); + memset(&addr6, 0, sockaddr_size); + addr6.sin6_family = AF_INET6; + addr6.sin6_port = htons(port); + if (net_string_to_address(AF_INET6, taddr, &addr6.sin6_addr) <= 0) + { + hub_log(log_fatal, "Unable to convert socket address (ipv6)"); + return 0; + } + + memcpy(addr, &addr6, sockaddr_size); + *addr_len = sockaddr_size; + + } + else if (ip_is_valid_ipv4(taddr)) + { + sockaddr_size = sizeof(struct sockaddr_in); + memset(&addr4, 0, sockaddr_size); + addr4.sin_family = AF_INET; + addr4.sin_port = htons(port); + if (net_string_to_address(AF_INET, taddr, &addr4.sin_addr) <= 0) + { + hub_log(log_fatal, "Unable to convert socket address (ipv4)"); + return 0; + } + memcpy(addr, &addr4, sockaddr_size); + *addr_len = sockaddr_size; + } + else + { + addr = 0; + *addr_len = 0; + return -1; + } + return 0; +} + + +int ip_mask_create_left(int af, int bits, struct ip_addr_encap* result) +{ + uint32_t mask; + int fill, remain_bits, n; + + memset(result, 0, sizeof(struct ip_addr_encap)); + result->af = af; + + if (bits < 0) bits = 0; + + if (af == AF_INET) + { + if (bits > 32) bits = 32; + mask = (0xffffffff << (32 - bits)); + if (bits == 0) mask = 0; + + result->internal_ip_data.in.s_addr = (((uint8_t*) &mask)[0] << 24) | (((uint8_t*) &mask)[1] << 16) | (((uint8_t*) &mask)[2] << 8) | (((uint8_t*) &mask)[3] << 0); + } + else if (af == AF_INET6) + { + if (bits > 128) bits = 128; + + fill = (128-bits) / 8; + remain_bits = (128-bits) % 8; + mask = (0xff << (8 - remain_bits)); + n = 0; + + for (n = 0; n < fill; n++) + ((uint8_t*) &result->internal_ip_data.in6)[n] = (uint8_t) 0xff; + + if (fill < 16) + ((uint8_t*) &result->internal_ip_data.in6)[fill] = (uint8_t) mask; + } + else + { + return -1; + } + +#ifdef IP_CALC_DEBUG + char* r_str = hub_strdup(ip_convert_to_string(result)); + hub_log(log_debug, "Created left mask: %s", r_str); + hub_free(r_str); +#endif + + return 0; +} + + +int ip_mask_create_right(int af, int bits, struct ip_addr_encap* result) +{ + uint32_t mask; + int fill, remain_bits, n, start; + uint8_t mask8; + + memset(result, 0, sizeof(struct ip_addr_encap)); + result->af = af; + + if (bits < 0) bits = 0; + + if (af == AF_INET) + { + if (bits > 32) bits = 32; + mask = (0xffffffff >> (32-bits)); + if (bits == 0) mask = 0; + result->internal_ip_data.in.s_addr = (((uint8_t*) &mask)[0] << 24) | (((uint8_t*) &mask)[1] << 16) | (((uint8_t*) &mask)[2] << 8) | (((uint8_t*) &mask)[3] << 0); + + } + else if (af == AF_INET6) + { + if (bits > 128) bits = 128; + + fill = (128-bits) / 8; + remain_bits = (128-bits) % 8; + mask8 = (0xff >> (8 - remain_bits)); + n = 0; + start = 16-fill; + + for (n = 0; n < start; n++) + ((uint8_t*) &result->internal_ip_data.in6)[n] = (uint8_t) 0x00; + + for (n = start; n < 16; n++) + ((uint8_t*) &result->internal_ip_data.in6)[n] = (uint8_t) 0xff; + + if (start > 0) + ((uint8_t*) &result->internal_ip_data.in6)[start-1] = (uint8_t) mask8; + } + else + { + return -1; + } + +#ifdef IP_CALC_DEBUG + char* r_str = hub_strdup(ip_convert_to_string(result)); + hub_log(log_debug, "Created right mask: %s", r_str); + hub_free(r_str); +#endif + + return 0; +} + + +void ip_mask_apply_AND(struct ip_addr_encap* addr, struct ip_addr_encap* mask, struct ip_addr_encap* result) +{ + memset(result, 0, sizeof(struct ip_addr_encap)); + result->af = addr->af; + + if (addr->af == AF_INET) + { + result->internal_ip_data.in.s_addr = addr->internal_ip_data.in.s_addr & mask->internal_ip_data.in.s_addr; + } + else if (addr->af == AF_INET6) + { + uint32_t A, B, C, D; + int n = 0; + int offset = 0; + for (n = 0; n < 4; n++) + { + offset = n * 4; + + A = (((uint8_t*) &addr->internal_ip_data.in6)[offset+0] << 24) | + (((uint8_t*) &addr->internal_ip_data.in6)[offset+1] << 16) | + (((uint8_t*) &addr->internal_ip_data.in6)[offset+2] << 8) | + (((uint8_t*) &addr->internal_ip_data.in6)[offset+3] << 0); + + B = (((uint8_t*) &mask->internal_ip_data.in6)[offset+0] << 24) | + (((uint8_t*) &mask->internal_ip_data.in6)[offset+1] << 16) | + (((uint8_t*) &mask->internal_ip_data.in6)[offset+2] << 8) | + (((uint8_t*) &mask->internal_ip_data.in6)[offset+3] << 0); + + C = A & B; + + D = (((uint8_t*) &C)[0] << 24) | + (((uint8_t*) &C)[1] << 16) | + (((uint8_t*) &C)[2] << 8) | + (((uint8_t*) &C)[3] << 0); + ((uint32_t*) &result->internal_ip_data.in6)[n] = D; + } + } +} + + +void ip_mask_apply_OR(struct ip_addr_encap* addr, struct ip_addr_encap* mask, struct ip_addr_encap* result) +{ + memset(result, 0, sizeof(struct ip_addr_encap)); + result->af = addr->af; + + if (addr->af == AF_INET) + { + result->internal_ip_data.in.s_addr = addr->internal_ip_data.in.s_addr | mask->internal_ip_data.in.s_addr; + } + else if (addr->af == AF_INET6) + { + uint32_t A, B, C, D; + int n = 0; + int offset = 0; + for (n = 0; n < 4; n++) + { + offset = n * 4; + + A = (((uint8_t*) &addr->internal_ip_data.in6)[offset+0] << 24) | + (((uint8_t*) &addr->internal_ip_data.in6)[offset+1] << 16) | + (((uint8_t*) &addr->internal_ip_data.in6)[offset+2] << 8) | + (((uint8_t*) &addr->internal_ip_data.in6)[offset+3] << 0); + + B = (((uint8_t*) &mask->internal_ip_data.in6)[offset+0] << 24) | + (((uint8_t*) &mask->internal_ip_data.in6)[offset+1] << 16) | + (((uint8_t*) &mask->internal_ip_data.in6)[offset+2] << 8) | + (((uint8_t*) &mask->internal_ip_data.in6)[offset+3] << 0); + + C = A | B; + + D = (((uint8_t*) &C)[0] << 24) | + (((uint8_t*) &C)[1] << 16) | + (((uint8_t*) &C)[2] << 8) | + (((uint8_t*) &C)[3] << 0); + ((uint32_t*) &result->internal_ip_data.in6)[n] = D; + } + } +} + + +int ip_compare(struct ip_addr_encap* a, struct ip_addr_encap* b) +{ + int ret = 0; + uint32_t A, B; + + if (a->af == AF_INET) + { + A = (((uint8_t*) &a->internal_ip_data.in.s_addr)[0] << 24) | + (((uint8_t*) &a->internal_ip_data.in.s_addr)[1] << 16) | + (((uint8_t*) &a->internal_ip_data.in.s_addr)[2] << 8) | + (((uint8_t*) &a->internal_ip_data.in.s_addr)[3] << 0); + + B = (((uint8_t*) &b->internal_ip_data.in.s_addr)[0] << 24) | + (((uint8_t*) &b->internal_ip_data.in.s_addr)[1] << 16) | + (((uint8_t*) &b->internal_ip_data.in.s_addr)[2] << 8) | + (((uint8_t*) &b->internal_ip_data.in.s_addr)[3] << 0); + ret = A - B; + } + else if (a->af == AF_INET6) + { + int n = 0; + int offset = 0; + for (n = 0; n < 4; n++) + { + offset = n * 4; + A = (((uint8_t*) &a->internal_ip_data.in6)[offset+0] << 24) | + (((uint8_t*) &a->internal_ip_data.in6)[offset+1] << 16) | + (((uint8_t*) &a->internal_ip_data.in6)[offset+2] << 8) | + (((uint8_t*) &a->internal_ip_data.in6)[offset+3] << 0); + + B = (((uint8_t*) &b->internal_ip_data.in6)[offset+0] << 24) | + (((uint8_t*) &b->internal_ip_data.in6)[offset+1] << 16) | + (((uint8_t*) &b->internal_ip_data.in6)[offset+2] << 8) | + (((uint8_t*) &b->internal_ip_data.in6)[offset+3] << 0); + + if (A == B) continue; + + return A - B; + } + return 0; + } + +#ifdef IP_CALC_DEBUG + char* a_str = hub_strdup(ip_convert_to_string(a)); + char* b_str = hub_strdup(ip_convert_to_string(b)); + hub_log(log_debug, "Comparing IPs '%s' AND '%s' => %d", a_str, b_str, ret); + hub_free(a_str); + hub_free(b_str); +#endif + + return ret; +} + + + + + + + + + + + diff --git a/src/ipcalc.h b/src/ipcalc.h new file mode 100644 index 0000000..b5f35e5 --- /dev/null +++ b/src/ipcalc.h @@ -0,0 +1,84 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +/* + * This file is used for fiddling with IP-addresses, + * primarily used for IP-banning in uhub. + */ + +#ifndef HAVE_UHUB_IPCALC_H +#define HAVE_UHUB_IPCALC_H + +struct ip_addr_encap { + int af; + union { + struct in_addr in; + struct in6_addr in6; + } internal_ip_data; +}; + + +extern int ip_convert_to_binary(const char* text_addr, struct ip_addr_encap* raw); + +extern char* ip_convert_to_string(struct ip_addr_encap* raw); + + +/* + * @return 1 if address is a valid IPv4 address in text notation + * 0 if invalid + */ +extern int ip_is_valid_ipv4(const char* address); + +/* + * @return 1 if address is a valid IPv6 address in text notation + * 0 if invalid + */ +extern int ip_is_valid_ipv6(const char* address); + + +/* + * This function converts an IP address in text_address to a binary + * struct sockaddr. + * This will auto-detect if the IP-address is IPv6 (and that is supported), + * or if IPv4 should be used. + * NOTE: Use sockaddr_storage to allocate enough memory for IPv6. + * + * @param text_addr is an ipaddress either ipv6 or ipv4. + * Special magic addresses called "any" and "loopback" exist, + * and will work accross IPv6/IPv4. + * @param port Fill the struct sockaddr* with the given port, can safely be ignored. + */ +extern int ip_convert_address(const char* text_address, int port, struct sockaddr* addr, socklen_t* addr_len); + + +extern int ip_mask_create_left(int af, int bits, struct ip_addr_encap* result); +extern int ip_mask_create_right(int af, int bits, struct ip_addr_encap* result); + +extern void ip_mask_apply_AND(struct ip_addr_encap* address, struct ip_addr_encap* mask, struct ip_addr_encap* result); +extern void ip_mask_apply_OR(struct ip_addr_encap* address, struct ip_addr_encap* mask, struct ip_addr_encap* result); + +/** + * @return <0 if a is less than b + * @return >0 if a is greater than b + * @return 0 if they are equal + */ +extern int ip_compare(struct ip_addr_encap* a, struct ip_addr_encap* b); + +#endif /* HAVE_UHUB_IPCALC_H */ + diff --git a/src/list.c b/src/list.c new file mode 100644 index 0000000..08d18c9 --- /dev/null +++ b/src/list.c @@ -0,0 +1,192 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#include "uhub.h" + +struct linked_list* list_create() +{ + struct linked_list* list = NULL; + list = (struct linked_list*) hub_malloc_zero(sizeof(struct linked_list)); + if (list == NULL) + return NULL; + return list; +} + + +void list_destroy(struct linked_list* list) +{ + if (list) + hub_free(list); +} + + +void list_clear(struct linked_list* list, void (*free_handle)(void* ptr)) +{ + struct node* node = list->first; + struct node* tmp = NULL; + while (node) + { + tmp = node->next; + free_handle(node->ptr); + hub_free(node); + node = tmp; + } + memset(list, 0, sizeof(struct linked_list)); +} + + +void list_append(struct linked_list* list, void* data_ptr) +{ + struct node* new_node = (struct node*) hub_malloc_zero(sizeof(struct node)); + new_node->ptr = data_ptr; + + if (list->last) + { + list->last->next = new_node; + new_node->prev = list->last; + } + else + { + list->first = new_node; + } + + list->last = new_node; + list->size++; +} + + +void list_remove(struct linked_list* list, void* data_ptr) +{ + struct node* node = list->first; + assert(data_ptr); + + list->iterator = NULL; + + while (node) + { + if (node->ptr == data_ptr) + { + if (node->next) + node->next->prev = node->prev; + + if (node->prev) + node->prev->next = node->next; + + if (node == list->last) + list->last = node->prev; + + if (node == list->first) + list->first = node->next; + + hub_free(node); + + list->size--; + break; + } + node = node->next; + } +} + + +size_t list_size(struct linked_list* list) +{ + return list->size; +} + + +void* list_get_index(struct linked_list* list, size_t offset) +{ + struct node* node = list->first; + size_t n = 0; + for (n = 0; n < list->size; n++) + { + if (n == offset) + { + return node->ptr; + } + node = node->next; + } + return NULL; +} + + +void* list_get_first(struct linked_list* list) +{ + list->iterator = list->first; + if (list->iterator == NULL) + return NULL; + + return list->iterator->ptr; +} + +struct node* list_get_first_node(struct linked_list* list) +{ + list->iterator = list->first; + if (list->iterator == NULL) + return NULL; + + return list->iterator; +} + +void* list_get_last(struct linked_list* list) +{ + list->iterator = list->last; + if (list->iterator == NULL) + return NULL; + + return list->iterator->ptr; +} + +struct node* list_get_last_node(struct linked_list* list) +{ + list->iterator = list->last; + if (list->iterator == NULL) + return NULL; + + return list->iterator; + +} + +void* list_get_next(struct linked_list* list) +{ + if (list->iterator == NULL) + list->iterator = list->first; + else + list->iterator = list->iterator->next; + + if (list->iterator == NULL) + return NULL; + + return list->iterator->ptr; +} + + +void* list_get_prev(struct linked_list* list) +{ + if (list->iterator == NULL) + return NULL; + + list->iterator = list->iterator->prev; + + if (list->iterator == NULL) + return NULL; + + return list->iterator->ptr; +} + diff --git a/src/list.h b/src/list.h new file mode 100644 index 0000000..7ea9de1 --- /dev/null +++ b/src/list.h @@ -0,0 +1,61 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#ifndef HAVE_UHUB_LINKED_LIST_H +#define HAVE_UHUB_LINKED_LIST_H + +struct linked_list +{ + size_t size; + struct node* first; + struct node* last; + struct node* iterator; +}; + +struct node +{ + void* ptr; + struct node* next; + struct node* prev; +}; + +extern struct linked_list* list_create(); +extern void list_destroy(struct linked_list*); +extern void list_clear(struct linked_list*, void (*free_handle)(void* ptr) ); + + +extern void list_append(struct linked_list* list, void* data_ptr); + +/** + * Remove data_ptr from the list. If multiple versions occur, only the first one is removed. + */ +extern void list_remove(struct linked_list* list, void* data_ptr); +extern size_t list_size(struct linked_list* list); + +extern void* list_get_index(struct linked_list*, size_t offset); +extern void* list_get_first(struct linked_list*); +extern void* list_get_last(struct linked_list*); +extern void* list_get_next(struct linked_list*); +extern void* list_get_prev(struct linked_list*); + +extern struct node* list_get_first_node(struct linked_list*); +extern struct node* list_get_last_node(struct linked_list*); + +#endif /* HAVE_UHUB_LINKED_LIST_H */ + diff --git a/src/log.c b/src/log.c new file mode 100644 index 0000000..072a757 --- /dev/null +++ b/src/log.c @@ -0,0 +1,231 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#include "uhub.h" +#include + +#ifndef WIN32 +#include +static int use_syslog = 0; +#endif + +static int verbosity = 4; +static FILE* logfile = NULL; + +#ifdef MEMORY_DEBUG +static FILE* memfile = NULL; +#define MEMORY_DEBUG_FILE "memlog.txt" +#endif + +#ifdef NETWORK_DUMP_DEBUG +#define NETWORK_DUMP_FILE "netdump.log" +static FILE* netdump = NULL; +#endif + + +static const char* prefixes[] = +{ + "FATAL", + "ERROR", + "WARN", + "USER", + "INFO", + "DEBUG", + "TRACE", + "DUMP", + "MEM", + "PROTO", + 0 +}; + + +void hub_log_initialize(const char* file, int syslog) +{ + + setlocale(LC_ALL, "C"); + +#ifdef MEMORY_DEBUG + memfile = fopen(MEMORY_DEBUG_FILE, "w"); + if (!memfile) + { + fprintf(stderr, "Unable to create " MEMORY_DEBUG_FILE " for logging memory allocations\n"); + return; + } +#endif + +#ifdef NETWORK_DUMP_DEBUG + netdump = fopen(NETWORK_DUMP_FILE, "w"); + if (!netdump) + { + fprintf(stderr, "Unable to create " NETWORK_DUMP_FILE " for logging network traffic\n"); + return; + } +#endif + +#ifndef WIN32 + if (syslog) + { + use_syslog = 1; + openlog("uhub", LOG_PID, LOG_USER); + } +#endif + + + if (!file) + { + logfile = stderr; + return; + } + + logfile = fopen(file, "a"); + if (!logfile) + { + logfile = stderr; + return; + } + +} + + +void hub_log_shutdown() +{ + if (logfile && logfile != stderr) + { + fclose(logfile); + logfile = NULL; + } + +#ifdef MEMORY_DEBUG + if (memfile) + { + fclose(memfile); + memfile = NULL; + } +#endif + +#ifdef NETWORK_DUMP_DEBUG + if (netdump) + { + fclose(netdump); + netdump = NULL; + } +#endif + +#ifndef WIN32 + if (use_syslog) + { + use_syslog = 0; + closelog(); + } +#endif +} + + +void hub_set_log_verbosity(int verb) +{ + verbosity = verb; +} + +void hub_log(int log_verbosity, const char *format, ...) +{ + static char logmsg[1024]; + static char timestamp[32]; + struct tm *tmp; + time_t t; + va_list args; + +#ifdef MEMORY_DEBUG + if (memfile && log_verbosity == log_memory) + { + va_start(args, format); + vsnprintf(logmsg, 1024, format, args); + va_end(args); + fprintf(memfile, "%s\n", logmsg); + fflush(memfile); + return; + } +#endif + +#ifdef NETWORK_DUMP_DEBUG + if (netdump && log_verbosity == log_protocol) + { + va_start(args, format); + vsnprintf(logmsg, 1024, format, args); + va_end(args); + fprintf(netdump, "%s\n", logmsg); + fflush(netdump); + return; + } +#endif + + if (log_verbosity < verbosity) + { + t = time(NULL); + tmp = gmtime(&t); + strftime(timestamp, 32, "%a, %d %b %Y %H:%M:%S +0000", tmp); + va_start(args, format); + vsnprintf(logmsg, 1024, format, args); + va_end(args); + + if (logfile) + { + fprintf(logfile, "%s %5s: %s\n", timestamp, prefixes[log_verbosity], logmsg); + fflush(logfile); + } + else + { + fprintf(stderr, "%s %5s: %s\n", timestamp, prefixes[log_verbosity], logmsg); + } + } + +#ifndef WIN32 + if (use_syslog) + { + int level = 0; + + if (verbosity < log_info) + return; + + va_start(args, format); + vsnprintf(logmsg, 1024, format, args); + va_end(args); + + switch (log_verbosity) + { + case log_fatal: level = LOG_CRIT; break; + case log_error: level = LOG_ERR; break; + case log_warning: level = LOG_WARNING; break; + case log_user: level = LOG_INFO | LOG_AUTH; break; + case log_info: level = LOG_INFO; break; + case log_debug: level = LOG_DEBUG; break; + + default: + level = 0; + break; + } + + if (level == 0) + return; + + level |= (LOG_USER | LOG_DAEMON); + syslog(level, "%s", logmsg); + } +#endif + +} diff --git a/src/log.h b/src/log.h new file mode 100644 index 0000000..5456aea --- /dev/null +++ b/src/log.h @@ -0,0 +1,59 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#ifndef HAVE_UHUB_LOG_H +#define HAVE_UHUB_LOG_H + +enum log_verbosity { + log_fatal = 0, + log_error = 1, + log_warning = 2, + log_user = 3, + log_info = 4, + log_debug = 5, + log_trace = 6, + log_dump = 7, + log_memory = 8, + log_protocol = 9, +}; + +/** + * Specify a minimum log verbosity for what messages should + * be printed in the log. + */ +extern void hub_set_log_verbosity(int log_verbosity); + +/** + * Print a message in the log. + */ +extern void hub_log(int log_verbosity, const char *format, ...); + +/** + * Initialize the log subsystem, if no output file is given (file is null) + * stderr is assumed by default. + * If the file cannot be opened for writing, stdout is also used. + */ +extern void hub_log_initialize(const char* file, int syslog); + +/** + * Shut down and close the logfile. + */ +extern void hub_log_shutdown(); + +#endif /* HAVE_UHUB_LOG_H */ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..505fdd6 --- /dev/null +++ b/src/main.c @@ -0,0 +1,441 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#include "uhub.h" + + +static int arg_verbose = 5; +static int arg_fork = 0; +static int arg_check_config = 0; +static int arg_dump_config = 0; +static int arg_have_config = 0; +static const char* arg_uid = 0; +static const char* arg_gid = 0; +static const char* arg_config = 0; +static const char* arg_log = 0; +static int arg_log_syslog = 0; + + +#ifndef WIN32 +void hub_handle_signal(int fd, short events, void* arg) +{ + struct hub_info* hub = (struct hub_info*) arg; + int signal = fd; + struct timeval now = {0, 0}; + + switch (signal) + { + case SIGINT: + hub_log(log_info, "Interrupted. Shutting down..."); + hub->status = hub_status_shutdown; + event_loopexit(&now); + break; + + case SIGTERM: + hub_log(log_info, "Terminated. Shutting down..."); + hub->status = hub_status_shutdown; + event_loopexit(&now); + break; + + case SIGPIPE: + hub_log(log_trace, "hub_handle_signal(): caught SIGPIPE (ignoring)"); + break; + + case SIGHUP: + hub_log(log_info, "Caught hangup signal. Reloading configuration files..."); + hub->status = hub_status_restart; + event_loopexit(&now); + break; + + case SIGUSR1: + hub_log(log_trace, "hub_handle_signal(): caught SIGUSR1 -- FIXME"); + break; + + case SIGUSR2: + hub_log(log_trace, "hub_handle_signal(): caught SIGUSR2"); + { + user_manager_stats(hub); + } + break; + + default: + hub_log(log_trace, "hub_handle_signal(): caught unknown signal: %d", signal); + hub->status = hub_status_shutdown; + event_loopexit(&now); + break; + } +} + +static struct event signal_events[10]; +static int signals[] = +{ + SIGINT, /* Interrupt the application */ + SIGTERM, /* Terminate the application */ + SIGPIPE, /* prevent sigpipe from kills the application */ + SIGHUP, /* reload configuration */ + SIGUSR1, /* dump statistics */ + SIGUSR2, /* (unused) */ + 0 +}; + +void setup_signal_handlers(struct hub_info* hub) +{ + int i = 0; + for (i = 0; signals[i]; i++) + { + signal_set(&signal_events[i], signals[i], hub_handle_signal, hub); + if (signal_add(&signal_events[i], NULL)) + { + hub_log(log_error, "Error setting signal handler %d", signals[i]); + } + } +} + +void shutdown_signal_handlers(struct hub_info* hub) +{ + int i = 0; + for (i = 0; signals[i]; i++) + { + signal_del(&signal_events[i]); + } +} + +#endif /* WIN32 */ + + +int main_loop() +{ + struct hub_config configuration; + struct acl_handle acl; + struct hub_info* hub = 0; + + if (net_initialize() == -1) + return -1; + + do + { + if (read_config(arg_config, &configuration, !arg_have_config) == -1) + return -1; + + if (acl_initialize(&configuration, &acl) == -1) + return -1; + + /* + * Don't restart networking when re-reading configuration. + * This might not be possible either, since we might have + * dropped our privileges to do so. + */ + if (!hub) + { + hub = hub_start_service(&configuration); + if (!hub) + return -1; +#ifndef WIN32 + setup_signal_handlers(hub); +#endif + } + + hub_set_variables(hub, &acl); + + event_dispatch(); + + hub_free_variables(hub); + acl_shutdown(&acl); + free_config(&configuration); + + } while(hub->status != hub_status_shutdown); + +#ifndef WIN32 + shutdown_signal_handlers(hub); +#endif + + if (hub) + { + hub_shutdown_service(hub); + } + + net_shutdown(); + hub_log_shutdown(); + return 0; +} + + +int check_configuration(int dump) +{ + struct hub_config configuration; + int ret = read_config(arg_config, &configuration, 0); + + if (dump) + { + dump_config(&configuration, dump > 1); + puts(""); + } + + if (ret == -1) + { + fprintf(stderr, "ERROR\n"); + return 1; + } + fprintf(stdout, "OK\n"); + return 0; +} + + +void print_version() +{ + fprintf(stdout, "" PRODUCT " " VERSION " " PRODUCT_TITLE "\n"); + fprintf(stdout, "Copyright (C) 2007-2009, Jan Vidar Krey \n" + "This is free software with ABSOLUTELY NO WARRANTY.\n\n"); + exit(0); +} + + +void print_usage(char* program) +{ + fprintf(stderr, "Usage: %s [options]\n\n", program); + fprintf(stderr, + "Options:\n" + " -v Verbose mode. Add more -v's for higher verbosity.\n" + " -q Quiet mode - no output\n" + " -f Fork to background\n" + " -l Log messages to given file (default: stderr)\n" + " -L Log messages to syslog\n" + " -c Specify configuration file (default: " SERVER_CONFIG ")\n" + " -C Check configuration and return\n" + " -s Show configuration parameters\n" + " -S Show configuration parameters, but ignore defaults\n" + " -h This message\n" +#ifndef WIN32 + " -u Run as given user\n" + " -g Run with given group permissions\n" +#endif + " -V Show version number.\n" + ); + + exit(0); +} + + +void parse_command_line(int argc, char** argv) +{ + int opt; + while ((opt = getopt(argc, argv, "vqfc:l:hu:g:VCsSL")) != -1) + { + switch (opt) + { + case 'V': + print_version(); + break; + + case 'v': + arg_verbose++; + break; + + case 'q': + arg_verbose -= 99; + break; + + case 'f': + arg_fork = 1; + break; + + case 'c': + arg_config = optarg; + arg_have_config = 1; + break; + + case 'C': + arg_check_config = 1; + arg_have_config = 1; + break; + + case 's': + arg_dump_config = 1; + arg_check_config = 1; + break; + + case 'S': + arg_dump_config = 2; + arg_check_config = 1; + break; + + case 'l': + arg_log = optarg; + break; + + case 'L': + arg_log_syslog = 1; + break; + + case 'h': + print_usage(argv[0]); + break; + + case 'u': + arg_uid = optarg; + break; + + case 'g': + arg_gid = optarg; + break; + + default: + print_usage(argv[0]); + break; + } + } + + if (arg_config == NULL) + { + arg_config = SERVER_CONFIG; + } + + hub_log_initialize(arg_log, arg_log_syslog); + hub_set_log_verbosity(arg_verbose); +} + + +#ifndef WIN32 +int drop_privileges() +{ + struct group* perm_group = 0; + struct passwd* perm_user = 0; + gid_t perm_gid = 0; + uid_t perm_uid = 0; + int gid_ok = 0; + int ret = 0; + + if (arg_gid) + { + ret = 0; + while ((perm_group = getgrent()) != NULL) + { + if (strcmp(perm_group->gr_name, arg_gid) == 0) + { + perm_gid = perm_group->gr_gid; + ret = 1; + break; + } + } + + endgrent(); + + if (!ret) + { + hub_log(log_fatal, "Unable to determine group id, check group name."); + return -1; + } + + hub_log(log_trace, "Setting group id %d (%s)", (int) perm_gid, arg_gid); + ret = setgid(perm_gid); + if (ret == -1) + { + hub_log(log_fatal, "Unable to change group id, permission denied."); + return -1; + } + gid_ok = 1; + } + + if (arg_uid) + { + ret = 0; + while ((perm_user = getpwent()) != NULL) + { + if (strcmp(perm_user->pw_name, arg_uid) == 0) + { + perm_uid = perm_user->pw_uid; + if (!gid_ok) + perm_gid = perm_user->pw_gid; + ret = 1; + break; + } + } + + endpwent(); + + if (!ret) + { + hub_log(log_fatal, "Unable to determine user id, check user name."); + return -1; + } + + if (!gid_ok) { + hub_log(log_trace, "Setting group id %d (%s)", (int) perm_gid, arg_gid); + ret = setgid(perm_gid); + if (ret == -1) + { + hub_log(log_fatal, "Unable to change group id, permission denied."); + return -1; + } + } + + hub_log(log_trace, "Setting user id %d (%s)", (int) perm_uid, arg_uid); + ret = setuid(perm_uid); + if (ret == -1) + { + hub_log(log_fatal, "Unable to change user id, permission denied."); + return -1; + } + } + + return 0; +} +#endif /* WIN32 */ + + +int main(int argc, char** argv) +{ + int ret = 0; + + parse_command_line(argc, argv); + + if (arg_check_config) + { + return check_configuration(arg_dump_config); + } + +#ifndef WIN32 + if (arg_fork) + { + ret = fork(); + if (ret == -1) + { + hub_log(log_fatal, "Unable to fork to background!"); + return -1; + } + else if (ret == 0) + { + /* child process */ + } + else + { + /* parent process */ + hub_log(log_debug, "Forked to background\n"); + return 0; + } + } + + if (drop_privileges() == -1) + return -1; +#endif /* WIN32 */ + + ret = main_loop(); + return ret; +} + diff --git a/src/memory.c b/src/memory.c new file mode 100644 index 0000000..349c433 --- /dev/null +++ b/src/memory.c @@ -0,0 +1,248 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#include "uhub.h" + +#ifdef MEMORY_DEBUG + +#define REALTIME_MALLOC_TRACKING + +#ifdef REALTIME_MALLOC_TRACKING +#define UHUB_MAX_ALLOCS 50000 +struct malloc_info +{ + void* ptr; + size_t size; + void* stack1; + void* stack2; +}; + +static int hub_alloc_count = 0; +static size_t hub_alloc_size = 0; +static int hub_alloc_peak_count = 0; +static size_t hub_alloc_peak_size = 0; +static size_t hub_alloc_oom = 0; + +static struct malloc_info hub_allocs[UHUB_MAX_ALLOCS] = { { 0, }, }; +static int malloc_slot = -1; /* free slot (-1, no slot) */ + +void internal_debug_print_leaks() +{ + size_t n = 0; + size_t leak = 0; + size_t count = 0; + hub_log(log_memory, "--- exit (allocs: %d, size: %zu) ---", hub_alloc_count, hub_alloc_size); + + for (; n < UHUB_MAX_ALLOCS; n++) + { + if (hub_allocs[n].ptr) + { + leak += hub_allocs[n].size; + count++; + hub_log(log_memory, "leak %p size: %zu (bt: %p %p)", hub_allocs[n].ptr, hub_allocs[n].size, hub_allocs[n].stack1, hub_allocs[n].stack2); + } + } + + hub_log(log_memory, "--- done (allocs: %d, size: %zu, peak: %d/%zu, oom: %zu) ---", count, leak, hub_alloc_peak_count, hub_alloc_peak_size, hub_alloc_oom); +} +#endif /* REALTIME_MALLOC_TRACKING */ + +void* internal_debug_mem_malloc(size_t size, const char* where) +{ + size_t n = 0; + char* ptr = malloc(size); + +#ifdef REALTIME_MALLOC_TRACKING + + /* Make sure the malloc info struct is initialized */ + if (!hub_alloc_count) + { + hub_log(log_memory, "--- start ---"); + for (n = 0; n < UHUB_MAX_ALLOCS; n++) + { + hub_allocs[n].ptr = 0; + hub_allocs[n].size = 0; + hub_allocs[n].stack1 = 0; + hub_allocs[n].stack2 = 0; + } + + atexit(&internal_debug_print_leaks); + } + + if (ptr) + { + if (malloc_slot != -1) + n = (size_t) malloc_slot; + else + n = 0; + + for (; n < UHUB_MAX_ALLOCS; n++) + { + if (!hub_allocs[n].ptr) + { + hub_allocs[n].ptr = ptr; + hub_allocs[n].size = size; + hub_allocs[n].stack1 = __builtin_return_address(1); + hub_allocs[n].stack2 = __builtin_return_address(2); + + hub_alloc_size += size; + hub_alloc_count++; + + hub_alloc_peak_count = MAX(hub_alloc_count, hub_alloc_peak_count); + hub_alloc_peak_size = MAX(hub_alloc_size, hub_alloc_peak_size); + + hub_log(log_memory, "%s %p (%d bytes) (bt: %p %p) {allocs: %d, size: %zu}", where, ptr, (int) size, hub_allocs[n].stack1, hub_allocs[n].stack2, hub_alloc_count, hub_alloc_size); + break; + } + } + } + else + { + hub_log(log_memory, "%s *** OOM for %d bytes", where, size); + hub_alloc_oom++; + return 0; + } +#endif /* REALTIME_MALLOC_TRACKING */ + return ptr; +} + +void internal_debug_mem_free(void* ptr) +{ +#ifdef REALTIME_MALLOC_TRACKING + size_t n = 0; + void* stack1 = __builtin_return_address(1); + void* stack2 = __builtin_return_address(2); + + if (!ptr) return; + + for (; n < UHUB_MAX_ALLOCS; n++) + { + if (hub_allocs[n].ptr == ptr) + { + hub_alloc_size -= hub_allocs[n].size; + hub_alloc_count--; + hub_allocs[n].ptr = 0; + hub_allocs[n].size = 0; + hub_allocs[n].stack1 = 0; + hub_allocs[n].stack2 = 0; + hub_log(log_memory, "free %p (bt: %p %p) {allocs: %d, size: %zu}", ptr, stack1, stack2, hub_alloc_count, hub_alloc_size); + malloc_slot = n; + free(ptr); + return; + } + } + + malloc_slot = -1; + abort(); + hub_log(log_memory, "free %p *** NOT ALLOCATED *** (bt: %p %p)", ptr, stack1, stack2); +#else + free(ptr); +#endif /* REALTIME_MALLOC_TRACKING */ +} + +char* debug_mem_strdup(const char* s) +{ + size_t size = strlen(s); + char* ptr = internal_debug_mem_malloc(size+1, "strdup"); + if (ptr) + { + memcpy(ptr, s, size); + ptr[size] = 0; + } + return ptr; +} + +char* debug_mem_strndup(const char* s, size_t n) +{ + size_t size = MIN(strlen(s), n); + char* ptr = internal_debug_mem_malloc(size+1, "strndup"); + if (ptr) + { + memcpy(ptr, s, size); + ptr[size] = 0; + } + return ptr; +} + +void* debug_mem_malloc(size_t size) +{ + void* ptr = internal_debug_mem_malloc(size, "malloc"); + return ptr; +} + +void debug_mem_free(void *ptr) +{ + internal_debug_mem_free(ptr); +} + + +#endif + +void* hub_malloc_zero(size_t size) +{ + void* data = hub_malloc(size); + if (data) + { + memset(data, 0, size); + } + return data; +} + +#ifdef DEBUG_FUNCTION_TRACE +#define FTRACE_LOG "ftrace.log" +static FILE* functrace = 0; + +void main_constructor() __attribute__ ((no_instrument_function, constructor)); +void main_deconstructor() __attribute__ ((no_instrument_function, destructor)); +void __cyg_profile_func_enter(void* frame, void* callsite) __attribute__ ((no_instrument_function)); +void __cyg_profile_func_exit(void* frame, void* callsite) __attribute__ ((no_instrument_function)); + + +void main_constructor() +{ + functrace = fopen(FTRACE_LOG, "w"); + if (functrace == NULL) + { + fprintf(stderr, "Cannot create function trace file: " FTRACE_LOG "\n"); + exit(-1); + } +} + + +void main_deconstructor() +{ + fclose(functrace); +} + + +void __cyg_profile_func_enter(void* frame, void* callsite) +{ + if (functrace) + fprintf(functrace, "E%p\n", frame); +} + +void __cyg_profile_func_exit(void* frame, void* callsite) +{ + if (functrace) + fprintf(functrace, "X%p\n", frame); +} + +#endif /* DEBUG_FUNCTION_TRACE */ + + diff --git a/src/memory.h b/src/memory.h new file mode 100644 index 0000000..0dee424 --- /dev/null +++ b/src/memory.h @@ -0,0 +1,45 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#ifndef HAVE_UHUB_MEMORY_HANDLER_H +#define HAVE_UHUB_MEMORY_HANDLER_H + +#ifdef MEMORY_DEBUG + +#define hub_malloc debug_mem_malloc +#define hub_free debug_mem_free +#define hub_strdup debug_mem_strdup +#define hub_strndup debug_mem_strndup +extern void* debug_mem_malloc(size_t size); +extern void debug_mem_free(void* ptr); +extern char* debug_mem_strdup(const char* s); +extern char* debug_mem_strndup(const char* s, size_t n); + +#else + +#define hub_malloc malloc +#define hub_free free +#define hub_strdup strdup +#define hub_strndup strndup + +#endif + +extern void* hub_malloc_zero(size_t size); + +#endif /* HAVE_UHUB_MEMORY_HANDLER_H */ diff --git a/src/message.c b/src/message.c new file mode 100644 index 0000000..231a910 --- /dev/null +++ b/src/message.c @@ -0,0 +1,836 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#include "uhub.h" + +#ifdef DEBUG +#define ADC_MSG_ASSERT(X) \ + uhub_assert(X); \ + uhub_assert(X->cache); \ + uhub_assert(X->capacity); \ + uhub_assert(X->length); \ + uhub_assert(X->length <= X->capacity); \ + uhub_assert(X->length == strlen(X->cache)); \ + uhub_assert(X->references >= 0); +#else +#define ADC_MSG_ASSERT(X) do { } while(0) +#endif + +struct adc_message* adc_msg_incref(struct adc_message* msg) +{ +#ifndef ADC_MESSAGE_INCREF + msg->references++; + return msg; +#else + struct adc_message* copy = adc_msg_copy(msg); + return copy; +#endif +} + +static void adc_msg_set_length(struct adc_message* msg, size_t len) +{ + msg->length = len; +} + +static int adc_msg_grow(struct adc_message* msg, size_t size) +{ + char* buf; + size_t newsize = 0; + + if (msg->capacity > size) + return 1; + + /* Make sure we align our data */ + newsize = size; + newsize += 2; /* termination */ + newsize += (newsize % sizeof(size_t)); /* alignment padding */ + + buf = hub_malloc_zero(newsize); + if (!buf) + return 0; + + if (msg->cache) + { + memcpy(buf, msg->cache, msg->length); + hub_free(msg->cache); + } + + msg->cache = buf; + msg->capacity = newsize; + return 1; +} + +/* NOTE: msg must be unterminated here */ +static int adc_msg_cache_append(struct adc_message* msg, const char* string, size_t len) +{ + if (!adc_msg_grow(msg, msg->length + len)) + { + /* FIXME: OOM! */ + return 0; + } + + memcpy(&msg->cache[msg->length], string, len); + adc_msg_set_length(msg, msg->length + len); + + assert(msg->capacity > msg->length); + msg->cache[msg->length] = 0; + return 1; +} + +/** + * Returns position of the first argument of the message. + * Excludes mandatory arguments for the given type of message + * like source and target. + */ +int adc_msg_get_arg_offset(struct adc_message* msg) +{ + if (!msg || !msg->cache) + return -1; + + switch (msg->cache[0]) + { + /* These *SHOULD* never be seen on a hub */ + case 'U': + case 'C': + return 4; /* Actually: 4 + strlen(cid). */ + + case 'I': + case 'H': + return 4; + + case 'B': + return 9; + + case 'F': + return (10 + (list_size(msg->feature_cast_include)*5) + (list_size(msg->feature_cast_exclude)*5)); + + case 'D': + case 'E': + return 14; + } + return -1; +} + + +int adc_msg_is_empty(struct adc_message* msg) +{ + int offset = adc_msg_get_arg_offset(msg); + + if (offset == -1) + return -1; + + if ((msg->length - 1) == (size_t) offset) + return 1; + + return 0; +} + + +void adc_msg_free(struct adc_message* msg) +{ + if (!msg) return; + + ADC_MSG_ASSERT(msg); + + if (msg->references > 0) + { + msg->references--; + } + else + { + hub_free(msg->cache); + + if (msg->feature_cast_include) + { + list_clear(msg->feature_cast_include, &hub_free); + list_destroy(msg->feature_cast_include); + msg->feature_cast_include = 0; + } + + if (msg->feature_cast_exclude) + { + list_clear(msg->feature_cast_exclude, &hub_free); + list_destroy(msg->feature_cast_exclude); + msg->feature_cast_exclude = 0; + } + + hub_free(msg); + } +} + + +struct adc_message* adc_msg_copy(const struct adc_message* cmd) +{ + char* tmp = 0; + struct adc_message* copy = (struct adc_message*) hub_malloc_zero(sizeof(struct adc_message)); + if (!copy) return NULL; /* OOM */ + + ADC_MSG_ASSERT(cmd); + + /* deep copy */ + copy->cmd = cmd->cmd; + copy->source = cmd->source; + copy->target = cmd->target; + copy->cache = 0; + copy->length = cmd->length; + copy->capacity = 0; + copy->priority = cmd->priority; + copy->references = 0; + copy->feature_cast_include = 0; + copy->feature_cast_exclude = 0; + + if (cmd->cache) + { + if (!adc_msg_grow(copy, copy->length)) + { + adc_msg_free(copy); + return NULL; /* OOM */ + } + memcpy(copy->cache, cmd->cache, cmd->length); + copy->cache[copy->length] = 0; + } + + if (cmd->feature_cast_include) + { + copy->feature_cast_include = list_create(); + tmp = list_get_first(cmd->feature_cast_include); + while (tmp) + { + list_append(copy->feature_cast_include, hub_strdup(tmp)); + tmp = list_get_next(cmd->feature_cast_include); + } + } + + if (cmd->feature_cast_exclude) + { + copy->feature_cast_exclude = list_create(); + tmp = list_get_first(cmd->feature_cast_exclude); + while (tmp) + { + list_append(copy->feature_cast_exclude, hub_strdup(tmp)); + tmp = list_get_next(cmd->feature_cast_exclude); + } + } + + ADC_MSG_ASSERT(copy); + + return copy; +} + + +struct adc_message* adc_msg_parse_verify(struct user* u, const char* line, size_t length) +{ + struct adc_message* command = adc_msg_parse(line, length); + + if (!command) + return 0; + + if (command->source && (!u || command->source != u->id.sid)) + { + adc_msg_free(command); + return 0; + } + + return command; +} + + +struct adc_message* adc_msg_parse(const char* line, size_t length) +{ + struct adc_message* command = (struct adc_message*) hub_malloc_zero(sizeof(struct adc_message)); + char prefix = line[0]; + size_t n = 0; + char temp_sid[5]; + int ok = 1; + int need_terminate = 0; + struct linked_list* feature_cast_list; + + if (command == NULL) + return NULL; /* OOM */ + + if (line[length-1] != '\n') + { + need_terminate = 1; + } + + if (!adc_msg_grow(command, length + need_terminate)) + { + hub_free(command); + return NULL; /* OOM */ + } + + adc_msg_set_length(command, length + need_terminate); + memcpy(command->cache, line, length); + + /* Ensure we are zero terminated */ + command->cache[length] = 0; + command->cache[length+need_terminate] = 0; + + command->cmd = FOURCC(line[0], line[1], line[2], line[3]); + command->priority = 0; + + switch (prefix) + { + case 'U': + case 'C': + /* these should never be seen on a hub */ + ok = 0; + break; + + case 'I': + case 'H': + ok = (length > 3); + break; + + case 'B': + ok = (length > 8 && + is_space(line[4]) && + is_valid_base32_char(line[5]) && + is_valid_base32_char(line[6]) && + is_valid_base32_char(line[7]) && + is_valid_base32_char(line[8])); + + if (!ok) break; + + temp_sid[0] = line[5]; + temp_sid[1] = line[6]; + temp_sid[2] = line[7]; + temp_sid[3] = line[8]; + temp_sid[4] = '\0'; + + command->source = string_to_sid(temp_sid); + break; + + case 'F': + ok = (length > 8 && + is_space(line[4]) && + is_valid_base32_char(line[5]) && + is_valid_base32_char(line[6]) && + is_valid_base32_char(line[7]) && + is_valid_base32_char(line[8])); + + if (!ok) break; + + temp_sid[0] = line[5]; + temp_sid[1] = line[6]; + temp_sid[2] = line[7]; + temp_sid[3] = line[8]; + temp_sid[4] = '\0'; + + command->source = string_to_sid(temp_sid); + + /* Create feature cast lists */ + command->feature_cast_include = list_create(); + command->feature_cast_exclude = list_create(); + + if (!command->feature_cast_include || !command->feature_cast_exclude) + { + list_destroy(command->feature_cast_include); + list_destroy(command->feature_cast_exclude); + hub_free(command->cache); + hub_free(command); + return NULL; /* OOM */ + } + + n = 10; + while (line[n] == '+' || line[n] == '-') + { + if (line[n++] == '+') + feature_cast_list = command->feature_cast_include; + else + feature_cast_list = command->feature_cast_exclude; + + temp_sid[0] = line[n++]; + temp_sid[1] = line[n++]; + temp_sid[2] = line[n++]; + temp_sid[3] = line[n++]; + temp_sid[4] = '\0'; + + list_append(feature_cast_list, hub_strdup(temp_sid)); + } + + if (n == 10) + ok = 0; + + break; + + case 'D': + case 'E': + ok = (length > 13 && + is_space(line[4]) && + is_valid_base32_char(line[5]) && + is_valid_base32_char(line[6]) && + is_valid_base32_char(line[7]) && + is_valid_base32_char(line[8]) && + is_space(line[9]) && + is_valid_base32_char(line[10]) && + is_valid_base32_char(line[11]) && + is_valid_base32_char(line[12]) && + is_valid_base32_char(line[13])); + + if (!ok) break; + + temp_sid[0] = line[5]; + temp_sid[1] = line[6]; + temp_sid[2] = line[7]; + temp_sid[3] = line[8]; + temp_sid[4] = '\0'; + + command->source = string_to_sid(temp_sid); + + temp_sid[0] = line[10]; + temp_sid[1] = line[11]; + temp_sid[2] = line[12]; + temp_sid[3] = line[13]; + temp_sid[4] = '\0'; + + command->target = string_to_sid(temp_sid); + break; + + default: + ok = 0; + } + + if (need_terminate) + { + command->cache[length] = '\n'; + } + + if (!ok) + { + adc_msg_free(command); + return NULL; + } + + /* At this point the arg_offset should point to a space, or the end of message */ + n = adc_msg_get_arg_offset(command); + if (command->cache[n] == ' ') + { + if (command->cache[n+1] == ' ') ok = 0; + } + else if (command->cache[n] == '\n') ok = 1; + else ok = 0; + + if (!ok) + { + adc_msg_free(command); + return NULL; + } + + ADC_MSG_ASSERT(command); + + return command; +} + + +struct adc_message* adc_msg_create(const char* line) +{ + return adc_msg_parse_verify(NULL, line, strlen(line)); +} + + +struct adc_message* adc_msg_construct(fourcc_t fourcc, size_t size) +{ + struct adc_message* msg = (struct adc_message*) hub_malloc_zero(sizeof(struct adc_message)); + + if (!msg) + return NULL; /* OOM */ + + if (size < sizeof(fourcc)) size = sizeof(fourcc); + + if (!adc_msg_grow(msg, size+1)) + { + hub_free(msg); + return NULL; /* OOM */ + } + + if (fourcc) + { + msg->cache[0] = (char) ((fourcc >> 24) & 0xff); + msg->cache[1] = (char) ((fourcc >> 16) & 0xff); + msg->cache[2] = (char) ((fourcc >> 8) & 0xff); + msg->cache[3] = (char) ((fourcc ) & 0xff); + msg->cache[4] = '\n'; + + /* Ensure we are zero terminated */ + adc_msg_set_length(msg, 5); + msg->cache[msg->length] = 0; + } + + msg->cmd = fourcc; + msg->priority = 0; + + return msg; +} + + +int adc_msg_remove_named_argument(struct adc_message* cmd, const char prefix_[2]) +{ + char* start; + char* end; + char* endInfo; + size_t endlen; + char prefix[4] = { ' ', prefix_[0], prefix_[1], '\0' }; + int found = 0; + int arg_offset = adc_msg_get_arg_offset(cmd); + size_t temp_len = 0; + + adc_msg_unterminate(cmd); + + start = memmem(&cmd->cache[arg_offset], (cmd->length - arg_offset), prefix, 3); + while (start) + { + endInfo = &cmd->cache[cmd->length]; + + if (&start[0] < &endInfo[0]) + { + end = memchr(&start[1], ' ', &endInfo[0]-&start[1]); + } + else + { + end = NULL; + } + + if (end) + { + + temp_len = &end[0] - &start[0]; // strlen(start); + /* hub_log(log_trace, " length=%d", (int) (temp_len)); */ + endlen = strlen(end); + + memmove(start, end, endlen); + start[endlen] = '\0'; + found++; + adc_msg_set_length(cmd, cmd->length - temp_len); + } + else + { + found++; + adc_msg_set_length(cmd, cmd->length - strlen(start)); + start[0] = '\0'; + break; + } + start = memmem(&cmd->cache[arg_offset], (cmd->length - arg_offset), prefix, 3); + } + + adc_msg_terminate(cmd); + + return found; +} + + +int adc_msg_has_named_argument(struct adc_message* cmd, const char prefix_[2]) +{ + int count = 0; + char* start; + char prefix[4] = { ' ', prefix_[0], prefix_[1], '\0' }; + int arg_offset = adc_msg_get_arg_offset(cmd); + + ADC_MSG_ASSERT(cmd); + + start = memmem(&cmd->cache[arg_offset], (cmd->length - arg_offset), prefix, 3); + while (start) + { + count++; + if ((&start[0] - &cmd->cache[0]) < 1+cmd->length) + start = memmem(&start[1], (&cmd->cache[cmd->length] - &start[0]), prefix, 3); + else + start = NULL; + } + + return count; +} + + +char* adc_msg_get_named_argument(struct adc_message* cmd, const char prefix_[2]) +{ + char* start; + char* end; + char* argument; + size_t length; + char prefix[4] = { ' ', prefix_[0], prefix_[1], '\0' }; + int arg_offset = adc_msg_get_arg_offset(cmd); + + ADC_MSG_ASSERT(cmd); + + start = memmem(&cmd->cache[arg_offset], cmd->length - arg_offset, prefix, 3); + if (!start) + return NULL; + + start = &start[3]; + end = strchr(start, ' '); + if (!end) end = &cmd->cache[cmd->length]; + length = &end[0] - &start[0]; + + argument = hub_strndup(start, length); + + if (length > 0 && argument[length-1] == '\n') + { + argument[length-1] = 0; + } + + return argument; +} + + +int adc_msg_replace_named_argument(struct adc_message* cmd, const char prefix[2], const char* string) +{ + ADC_MSG_ASSERT(cmd); + + while (adc_msg_has_named_argument(cmd, prefix)) + { + adc_msg_remove_named_argument(cmd, prefix); + } + + if (adc_msg_add_named_argument(cmd, prefix, string) == -1) + { + return -1; + } + + ADC_MSG_ASSERT(cmd); + + return 0; +} + + +void adc_msg_terminate(struct adc_message* cmd) +{ + if (cmd->cache[cmd->length - 1] != '\n') + { + adc_msg_cache_append(cmd, "\n", 1); + } + ADC_MSG_ASSERT(cmd); +} + +/* FIXME: this looks bogus */ +void adc_msg_unterminate(struct adc_message* cmd) +{ + ADC_MSG_ASSERT(cmd); + + if (cmd->length > 0 && cmd->cache[cmd->length-1] == '\n') + { + cmd->length--; + cmd->cache[cmd->length] = 0; + } +} + +int adc_msg_add_named_argument(struct adc_message* cmd, const char prefix[2], const char* string) +{ + int ret = 0; + if (!string) + return -1; + + ADC_MSG_ASSERT(cmd); + + adc_msg_unterminate(cmd); + adc_msg_cache_append(cmd, " ", 1); + adc_msg_cache_append(cmd, prefix, 2); + adc_msg_cache_append(cmd, string, strlen(string)); + adc_msg_terminate(cmd); + return ret; +} + + +int adc_msg_add_argument(struct adc_message* cmd, const char* string) +{ + ADC_MSG_ASSERT(cmd); + + adc_msg_unterminate(cmd); + adc_msg_cache_append(cmd, " ", 1); + adc_msg_cache_append(cmd, string, strlen(string)); + adc_msg_terminate(cmd); + return 0; +} + + +char* adc_msg_get_argument(struct adc_message* cmd, int offset) +{ + char* start; + char* end; + char* argument; + int count = 0; + + ADC_MSG_ASSERT(cmd); + + adc_msg_unterminate(cmd); + + start = strchr(&cmd->cache[adc_msg_get_arg_offset(cmd)-1], ' '); + while (start) + { + end = strchr(&start[1], ' '); + + if (count == offset) + { + if (end) + { + argument = hub_strndup(&start[1], (&end[0] - &start[1])); + } + else + { + argument = hub_strdup(&start[1]); + if (argument[strlen(argument)-1] == '\n') + argument[strlen(argument)-1] = 0; + } + + if (strlen(argument)) + { + adc_msg_terminate(cmd); + return argument; + } + } + + count++; + start = end; + } + + adc_msg_terminate(cmd); + return 0; +} + +/** + * NOTE: Untested code. + */ +int adc_msg_get_argument_index(struct adc_message* cmd, const char prefix[2]) +{ + char* start; + char* end; + int count = 0; + + ADC_MSG_ASSERT(cmd); + + adc_msg_unterminate(cmd); + + start = strchr(&cmd->cache[adc_msg_get_arg_offset(cmd)-1], ' '); + while (start) + { + end = strchr(&start[1], ' '); + if (((&end[0] - &start[1]) > 2) && ((start[1] == prefix[0]) && (start[2] == prefix[1]))) + { + adc_msg_terminate(cmd); + return count; + } + count++; + start = end; + } + adc_msg_terminate(cmd); + return -1; +} + + + +int adc_msg_escape_length(const char* str) +{ + int add = 0; + int n = 0; + for (; str[n]; n++) + if (str[n] == ' ' || str[n] == '\n' || str[n] == '\\') add++; + return n + add; +} + + +int adc_msg_unescape_length(const char* str) +{ + int add = 0; + int n = 0; + int escape = 0; + for (; str[n]; n++) + { + if (escape) + { + escape = 0; + } + else + { + if (str[n] == '\\') + { + escape = 1; + add++; + } + } + } + return n - add; +} + + + + + +char* adc_msg_unescape(const char* string) +{ + char* new_string = hub_malloc(adc_msg_unescape_length(string)+1); + char* ptr = (char*) new_string; + char* str = (char*) string; + int escaped = 0; + + while (*str) + { + if (escaped) { + if (*str == 's') + *ptr++ = ' '; + else if (*str == '\\') + *ptr++ = '\\'; + else if (*str == 'n') + *ptr++ = '\n'; + else + *ptr++ = *str; + + escaped = 0; + } else { + if (*str == '\\') + escaped = 1; + else + *ptr++ = *str; + + } + str++; + } + *ptr = 0; + return new_string; +} + + +char* adc_msg_escape(const char* string) +{ + char* str = hub_malloc(adc_msg_escape_length(string)+1); + int n = 0; + int i = 0; + for (i = 0; i < strlen(string); i++) + { + switch (string[i]) { + case '\\': /* fall through */ + str[n++] = '\\'; + str[n++] = '\\'; + break; + case '\n': + str[n++] = '\\'; + str[n++] = 'n'; + break; + case ' ': + str[n++] = '\\'; + str[n++] = 's'; + break; + default: + str[n++] = string[i]; + break; + } + } + str[n] = '\0'; + return str; +} + diff --git a/src/message.h b/src/message.h new file mode 100644 index 0000000..eccd173 --- /dev/null +++ b/src/message.h @@ -0,0 +1,206 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#ifndef HAVE_UHUB_COMMAND_H +#define HAVE_UHUB_COMMAND_H + +struct user; + +struct adc_message +{ + fourcc_t cmd; + sid_t source; + sid_t target; + char* cache; + size_t length; + size_t capacity; + size_t priority; + size_t references; + struct linked_list* feature_cast_include; + struct linked_list* feature_cast_exclude; +}; + +enum msg_status_level +{ + status_level_info = 0, /* Success/informative status message */ + status_level_error = 1, /* Recoverable error */ + status_level_fatal = 2, /* Fatal error (disconnect) */ +}; + +/** + * Increase the reference counter for an ADC message struct. + * NOTE: Always use the returned value, and not the passed value, as + * it ensures we can actually copy the value if needed. + */ +extern struct adc_message* adc_msg_incref(struct adc_message* msg); + +/** + * Decrease the reference counter, and free the memory when apropriate. + */ +extern void adc_msg_free(struct adc_message* msg); + +/** + * Perform deep copy a command. + * NOTE: 'references' will be zero for the copied command. + * @return a copy of cmd or NULL if not able to allocate memory. + */ +extern struct adc_message* adc_msg_copy(const struct adc_message* cmd); + +/** + * This will parse 'string' and return it as a adc_message struct, or + * NULL if not able to allocate memory or 'string' does not contain + * a valid ADC command. + * + * The message is only considered valid if the user who sent it + * is the rightful origin of the message. + */ +extern struct adc_message* adc_msg_parse_verify(struct user* u, const char* string, size_t length); + +/** + * This will parse 'string' and return it as a adc_message struct, or + * NULL if not able to allocate memory or 'string' does not contain + * a valid ADC command. + */ +extern struct adc_message* adc_msg_parse(const char* string, size_t length); + +/** + * This will construct a adc_message based on 'string'. + * Only to be used for server generated commands. + */ +extern struct adc_message* adc_msg_create(const char* string); + +/** + * Construct a message with the given 'fourcc' and allocate + * 'size' bytes for later use. + */ +extern struct adc_message* adc_msg_construct(fourcc_t fourcc, size_t size); + +/** + * Remove a named argument from the command. + * + * @arg prefix a 2 character argument prefix + * @return the number of named arguments removed. + */ +extern int adc_msg_remove_named_argument(struct adc_message* cmd, const char prefix[2]); + +/** + * Count the number of arguments matching the given 2 character prefix. + * + * @arg prefix a 2 character argument prefix + * @return the number of matching arguments + */ +extern int adc_msg_has_named_argument(struct adc_message* cmd, const char prefix[2]); + +/** + * Returns a named arguments based on the 2 character prefix. + * If multiple matching arguments exists, only the first one will be returned + * by this function. + * + * NOTE: Returned memory must be free'd with hub_free(). + * + * @arg prefix a 2 character argument prefix + * @return the argument or NULL if OOM/not found. + */ +extern char* adc_msg_get_named_argument(struct adc_message* cmd, const char prefix[2]); + +/** + * Returns a offset of an argument based on the 2 character prefix. + * If multiple matching arguments exists, only the first one will be returned + * by this function. + * + * @arg prefix a 2 character argument prefix + * @return the offset or -1 if the argument is not found. + */ +extern int adc_msg_get_named_argument_index(struct adc_message* cmd, const char prefix[2]); + +/** + * @param cmd command to be checked + * @return 1 if the command does not have any arguments (parameters), 0 otherwise, -1 if cmd is invalid. + */ +extern int adc_msg_is_empty(struct adc_message* cmd); + +/** + * Returns the argument on the offset position in the command. + * If offset is invalid NULL is returned. + * + * NOTE: Returned memory must be free'd with hub_free(). + * + * @return the argument or NULL if OOM/not found. + */ +extern char* adc_msg_get_argument(struct adc_message* cmd, int offset); + +/** + * Replace a named argument in the command. + * This will remove any matching arguments (multiple, or none), + * then add 'string' as an argument using the given prefix. + * + * @arg prefix a 2 character argument prefix + * @arg string must be escaped (see adc_msg_escape). + * @return 0 if successful, or -1 if an error occured. + */ +extern int adc_msg_replace_named_argument(struct adc_message* cmd, const char prefix[2], const char* string); + +/** + * Append an argument + * + * @arg string must be escaped (see adc_msg_escape). + * @return 0 if successful, or -1 if an error occured (out of memory). + */ +extern int adc_msg_add_argument(struct adc_message* cmd, const char* string); + +/** + * Append a named argument + * + * @arg prefix a 2 character argument prefix + * @arg string must be escaped (see adc_msg_escape). + * @return 0 if successful, or -1 if an error occured (out of memory). + */ +extern int adc_msg_add_named_argument(struct adc_message* cmd, const char prefix[2], const char* string); + +/** + * Convert a ADC command escaped string to a regular string. + * @return string or NULL if out of memory + */ +extern char* adc_msg_unescape(const char* string); + +/** + * Convert a string to a ADC command escaped string. + * @return adc command escaped string or NULL if out of memory. + */ +extern char* adc_msg_escape(const char* string); + +/** + * This will ensure a newline is at the end of the command. + */ +void adc_msg_terminate(struct adc_message* cmd); + +/** + * This will remove any newline from the end of the command + */ +void adc_msg_unterminate(struct adc_message* cmd); + +/** + * @return the offset for the first command argument in msg->cache. + * or -1 if the command is not understood. + * NOTE: for 'U' and 'C' commands (normally not seen by hubs), + * this returns 4. Should be 4 + lengthOf(cid). + */ +int adc_msg_get_arg_offset(struct adc_message* msg); + +#endif /* HAVE_UHUB_COMMAND_H */ diff --git a/src/misc.c b/src/misc.c new file mode 100644 index 0000000..3728c58 --- /dev/null +++ b/src/misc.c @@ -0,0 +1,299 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#include "uhub.h" + +int is_space(char c) +{ + if (c == ' ') return 1; + return 0; +} + +int is_white_space(char c) +{ + if (c == ' ' || c == '\t' || c == '\r') return 1; + return 0; +} + + +char* strip_white_space(char* string) +{ + char* pos; + + while (string[0] && is_white_space(string[0])) string++; + + if (!strlen(string)) + return string; + + /* Strip appending whitespace */ + pos = &string[strlen(string)-1]; + while (&string[0] < &pos[0] && is_white_space(pos[0])) { pos[0] = 0; pos--; } + + return string; +} + + +int is_valid_utf8(const char* string) +{ + int expect = 0; + char div = 0; + int pos = 0; + int length = strlen(string); + + if (length == 0) return 1; + + for (pos = 0; pos < strlen(string); pos++) + { + if (expect) + { + if ((string[pos] & 0xC0) == 0x80) expect--; + else return 0; + } + else + { + if (string[pos] & 0x80) + { + for (div = 0x40; div > 0x10; div /= 2) + { + if (string[pos] & div) expect++; + else break; + } + if ((string[pos] & div) || (pos+expect >= strlen(string))) return 0; + } + } + } + return 1; +} + + +int is_valid_base32_char(char c) +{ + if ((c >= 'A' && c <= 'Z') || (c >= '2' && c <= '7')) return 1; + return 0; +} + + +int is_num(char c) +{ + if (c >= '0' && c <= '9') return 1; + return 0; +} + + +void base32_encode(const unsigned char* buffer, size_t len, char* result) { + unsigned char word = 0; + size_t n = 0; + size_t i = 0; + size_t index = 0; + for (; i < len;) { + if (index > 3) { + word = (buffer[i] & (0xFF >> index)); + index = (index + 5) % 8; + word <<= index; + if (i < len - 1) + word |= buffer[i + 1] >> (8 - index); + i++; + } else { + word = (buffer[i] >> (8 - (index + 5))) & 0x1F; + index = (index + 5) % 8; + if (index == 0) i++; + } + result[n++] = BASE32_ALPHABET[word]; + } + result[n] = '\0'; +} + +void base32_decode(const char* src, unsigned char* dst, size_t len) { + size_t index = 0; + size_t offset = 0; + size_t i = 0; + memset(dst, 0, len); + for (i = 0; src[i]; i++) { + unsigned char n = 0; + for (; n < 32; n++) if (src[i] == BASE32_ALPHABET[n]) break; + if (n == 32) continue; + if (index <= 3) { + index = (index + 5) % 8; + if (index == 0) { + dst[offset++] |= n; + if (offset == len) break; + } else { + dst[offset] |= n << (8 - index); + } + } else { + index = (index + 5) % 8; + dst[offset++] |= (n >> index); + if (offset == len) break; + dst[offset] |= n << (8 - index); + } + } +} + + +int file_read_lines(const char* file, void* data, file_line_handler_t handler) +{ + int fd; + ssize_t ret; + char buf[MAX_RECV_BUF]; + char *pos, *start; + size_t line_count = 0; + + memset(buf, 0, MAX_RECV_BUF); + + hub_log(log_trace, "Opening file %s for line reading.", file); + + fd = open(file, 0); + if (fd == -1) + { + hub_log(log_error, "Unable to open file %s: %s", file, strerror(errno)); + return -2; + } + + ret = read(fd, buf, MAX_RECV_BUF); + if (ret < 0) + { + hub_log(log_error, "Unable to read from file %s: %s", file, strerror(errno)); + close(fd); + return -1; + } + else if (ret == 0) + { + close(fd); + hub_log(log_warning, "File is empty."); + return 0; + } + else + { + close(fd); + + /* Parse configuaration */ + start = buf; + while ((pos = strchr(start, '\n'))) + { + pos[0] = '\0'; + if (strlen(start) > 0) + { + hub_log(log_dump, "Line: %s", start); + if (handler(start, line_count+1, data) < 0) + return -1; + } + start = &pos[1]; + line_count++; + } + + if (strlen(start) > 0) + { + buf[strlen(start)] = 0; + hub_log(log_dump, "Line: %s", start); + if (handler(start, line_count+1, data) < 0) + return -1; + } + } + return line_count+1; +} + + +int uhub_atoi(const char* value) { + int len = strlen(value); + int offset = 0; + int val = 0; + int i = 0; + for (; i < len; i++) + if (value[i] > '9' || value[i] < '0') + offset++; + + for (i = offset; i< len; i++) + val = val*10 + (value[i] - '0'); + + return value[0] == '-' ? -val : val; +} + + +/* + * FIXME: -INTMIN is wrong! + */ +const char* uhub_itoa(int val) +{ + size_t i; + int value; + static char buf[22]; + memset(buf, 0, sizeof(buf)); + if (!val) + { + strcat(buf, "0"); + return buf; + } + i = sizeof(buf) - 1; + for (value = abs(val); value && i > 0; value /= 10) + buf[--i] = "0123456789"[value % 10]; + + if (val < 0 && i > 0) + buf[--i] = '-'; + return buf+i; +} + + +const char* uhub_ulltoa(uint64_t val) +{ + size_t i; + static char buf[22] = { 0, }; + memset(buf, 0, sizeof(buf)); + + if (!val) + { + strcat(buf, "0"); + return buf; + } + i = sizeof(buf) - 1; + for (; val && i > 0; val /= 10) + buf[--i] = "0123456789"[val % 10]; + return buf+i; +} + + + +#ifndef HAVE_STRNDUP +char* strndup(const char* string, size_t n) +{ + size_t max = MIN(strlen(string), n); + char* tmp = hub_malloc(max+1); + memcpy(tmp, string, max); + tmp[max] = 0; + return tmp; +} +#endif + +#ifndef HAVE_MEMMEM +void* memmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) +{ + char* c_buf = (char*) haystack; + char* c_pat = (char*) needle; + char* ptr = memchr(c_buf, c_pat[0], haystacklen); + + while (ptr && (&ptr[0] - &c_buf[0] < haystacklen)) + { + if (!memcmp(ptr, c_pat, needlelen)) + return ptr; + ptr = memchr(&ptr[1], c_pat[0], &c_buf[haystacklen] - &ptr[0]); + } + return 0; +} +#endif + + diff --git a/src/misc.h b/src/misc.h new file mode 100644 index 0000000..789c616 --- /dev/null +++ b/src/misc.h @@ -0,0 +1,58 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#ifndef HAVE_UHUB_MISC_H +#define HAVE_UHUB_MISC_H + +typedef int (*file_line_handler_t)(char* line, int line_number, void* data); + +extern int is_num(char c); +extern int is_space(char c); +extern int is_white_space(char c); +extern int is_valid_utf8(const char* string); +extern int is_valid_base32_char(char c); +extern void base32_encode(const unsigned char* buffer, size_t len, char* result); +extern void base32_decode(const char* src, unsigned char* dst, size_t len); +extern char* strip_white_space(char* string); + +extern int file_read_lines(const char* file, void* data, file_line_handler_t handler); + + +extern const char* uhub_itoa(int val); +extern const char* uhub_ulltoa(uint64_t val); + +extern int uhub_atoi(const char* value); + +#ifdef NEED_ATOLL +extern int atoll(const char* value); +#endif + + +#ifndef HAVE_STRNDUP +extern char* strndup(const char* string, size_t n); +#endif + +#ifndef HAVE_MEMMEM +void* memmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen); +#endif + + +#endif /* HAVE_UHUB_MISC_H */ + + diff --git a/src/netevent.c b/src/netevent.c new file mode 100644 index 0000000..5127b80 --- /dev/null +++ b/src/netevent.c @@ -0,0 +1,292 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#include "uhub.h" + + +void net_on_read(int fd, short ev, void *arg) +{ + static char buf[MAX_RECV_BUF]; + struct user* user = (struct user*) arg; + char* pos; + char* start; + ssize_t offset; + ssize_t size; + ssize_t buflen; + int more = 1; + int flag_close = 0; + + hub_log(log_trace, "net_on_read() : fd=%d, ev=%d, arg=%p", fd, (int) ev, arg); + + if (ev == EV_TIMEOUT) + { + more = 0; + if (user_is_connecting(user)) + { + flag_close = quit_timeout; + } + else + { + hub_send_ping(user); + } + } + + while (more) + { + offset = 0; + if (user->recv_buf) + { + memcpy(buf, user->recv_buf, user->recv_buf_offset); + offset = user->recv_buf_offset; + } + else + { + offset = 0; + } + + size = net_recv(fd, &buf[offset], MAX_RECV_BUF - offset, 0); + if (size == -1) + { + if (net_error() != EWOULDBLOCK) + flag_close = quit_socket_error; + break; + } + else if (size == 0) + { + flag_close = quit_disconnected; + break; + } + else + { + buflen = offset + size; + start = buf; + while ((pos = strchr(start, '\n'))) + { + pos[0] = '\0'; + if (strlen(start) > 0 && strlen(start) < user->hub->config->max_recv_buffer) + { + if (hub_handle_message(user, start, &pos[0]-&start[0]) == -1) + { + flag_close = quit_protocol_error; + more = 0; + break; + } + } + start = &pos[1]; + } + + if (!more) break; + + if (&buf[offset + size] > &start[0]) + { + if (!user->recv_buf) + { + user->recv_buf = hub_malloc(user->hub->config->max_recv_buffer); + } + + if (!user->recv_buf) + { + flag_close = quit_memory_error; + break; + } + else + { + memcpy(user->recv_buf, start, &buf[offset + size] - &start[0]); + user->recv_buf_offset = &buf[offset + size] - &start[0]; + } + } + else + { + if (user->recv_buf) + { + hub_free(user->recv_buf); + user->recv_buf = 0; + user->recv_buf_offset = 0; + } + } + } + } + + if (flag_close) + { + user_disconnect(user, flag_close); + return; + } + + if (user_is_logged_in(user)) + { + if (user->ev_read) + { + struct timeval timeout = { TIMEOUT_IDLE, 0 }; + event_add(user->ev_read, &timeout); + } + } + else if (user_is_connecting(user)) + { + if (user->ev_read) + { + struct timeval timeout = { TIMEOUT_HANDSHAKE, 0 }; + event_add(user->ev_read, &timeout); + } + } +} + + +void net_on_write(int fd, short ev, void *arg) +{ + struct user* user = (struct user*) arg; + struct adc_message* msg; + int ret; + int length; + int close_flag = 0; + + msg = list_get_first(user->send_queue); + while (msg) + { + length = msg->length - user->send_queue_offset; + ret = net_send(user->sd, &msg->cache[user->send_queue_offset], length, UHUB_SEND_SIGNAL); + + if (ret == 0 || (ret == -1 && net_error() == EWOULDBLOCK)) + { + close_flag = 0; + break; + } + else if (ret > 0) + { + + user->tm_last_write = time(NULL); + + if (ret == length) + { + user->send_queue_size -= ret; + user->send_queue_offset = 0; + list_remove(user->send_queue, msg); + + if (user_flag_get(user, flag_user_list) && (msg == user->info || user->send_queue_size == 0)) + { + user_flag_unset(user, flag_user_list); + } + + adc_msg_free(msg); + msg = 0; + + if (user->send_queue_size == 0) + break; + } + else + { + user->send_queue_size -= ret; + user->send_queue_offset -= ret; + break; + } + } + else + { + close_flag = quit_socket_error; + break; + } + msg = list_get_first(user->send_queue); + } + + + if (close_flag) + { + user_disconnect(user, close_flag); + } + else + { + if (user->send_queue_size > 0 && user->ev_write) + event_add(user->ev_write, NULL); + } +} + + +void net_on_accept(int server_fd, short ev, void *arg) +{ + struct hub_info* hub = (struct hub_info*) arg; + struct user* user = 0; + int accept_more = 1; + const char* addr; + struct timeval timeout = { TIMEOUT_CONNECTED, 0 }; + + while (accept_more) + { + int fd = net_accept(server_fd); + if (fd == -1) + { + if (net_error() == EWOULDBLOCK) + { + break; + } + else + { + hub_log(log_error, "Accept error: %d %s", net_error(), strerror(net_error())); + break; + } + } + + addr = net_get_peer_address(fd); + + /* FIXME: Should have a plugin log this */ + hub_log(log_trace, "Got connection from %s", addr); + + /* FIXME: A plugin should perform this check: is IP banned? */ + if (acl_is_ip_banned(hub->acl, addr)) + { + hub_log(log_info, "Denied [%s] (IP banned)", addr); + net_close(fd); + continue; + } + + user = user_create(hub, fd); + if (!user) + { + hub_log(log_error, "Unable to create user after socket accepted. Out of memory?"); + net_close(fd); + break; + } + + /* Store IP address in user object */ + ip_convert_to_binary(addr, &user->ipaddr); + + net_set_nonblocking(fd, 1); + net_set_nosigpipe(fd, 1); + + event_set(user->ev_read, fd, EV_READ | EV_PERSIST, net_on_read, user); + event_set(user->ev_write, fd, EV_WRITE, net_on_write, user); + event_add(user->ev_read, &timeout); + } +} + +#ifdef ADC_UDP_OPERATION +extern void net_on_packet(int fd, short ev, void *arg) +{ + static char buffer[1024] = {0,}; + // struct hub_info* hub = (struct hub_info*) arg; + // struct user* user = 0; + ssize_t size; + struct sockaddr_storage from; + socklen_t fromlen; + + size = recvfrom(fd, buffer, 1024, 0, (struct sockaddr*) &from, &fromlen); + + // FIXME: A plugin should handle this! + hub_log(log_info, "Datagram [%s] (%d bytes)", buffer, (int) size); +} +#endif diff --git a/src/netevent.h b/src/netevent.h new file mode 100644 index 0000000..142f089 --- /dev/null +++ b/src/netevent.h @@ -0,0 +1,53 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#ifndef HAVE_UHUB_NET_EVENT_H +#define HAVE_UHUB_NET_EVENT_H + +/** + * Network callback for reading data from a socket. + */ +extern void net_on_read(int fd, short ev, void *arg); + +/** + * Network callback for writing data to a socket. + */ +extern void net_on_write(int fd, short ev, void *arg); + +/** + * Network callback for timers. + */ +extern void net_on_read_timeout(int fd, short ev, void* arg); + + +/** + * Network callback to accept incoming connections. + */ +extern void net_on_accept(int fd, short ev, void *arg); + +#ifdef ADC_UDP_OPERATION +/** + * Network callback to receive incoming UDP datagram. + */ +extern void net_on_packet(int fd, short ev, void *arg); +#endif + + +#endif /* HAVE_UHUB_NET_EVENT_H */ + diff --git a/src/network-epoll.c b/src/network-epoll.c new file mode 100644 index 0000000..2c74e3a --- /dev/null +++ b/src/network-epoll.c @@ -0,0 +1,326 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007, Jan Vidar Krey + * + * 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 . + * + */ + +#include "uhub.h" + +#ifdef HAVE_EPOLL + +// #define DEBUG_EPOLL +static struct epoll_event* events = 0; +static int epfd = -1; + +#ifdef DEBUG_EPOLL +static void dump_listeners() +{ + int i; + struct net_event_listener* listener; + + + hub_log(log_dump, "listeners: number=%d", num_connections); + + for (i = 0; i < num_connections; i++) + { + listener = &listeners[i]; + + if (listener) + { + if (listener->fd != -1) + { + hub_log(log_dump, "epoll_dump_listeners: pos=%d/%d fd=%d, ptr=%p", i, num_connections, listeners->fd, listeners); + } + else + { + hub_log(log_dump, "epoll_dump_listeners: pos=%d/%d (unused)", i, num_connections); + } + + listener = 0; + } + } + + getc(stdin); + +} + +#endif + + +static void set_poll_events(struct epoll_event* handle, short trigger) +{ + memset(handle, 0, sizeof(struct epoll_event)); + + if (trigger & evt_accept || trigger & evt_read || trigger & evt_close) + handle->events |= EPOLLIN; + + if (trigger & evt_write) + handle->events |= EPOLLOUT; + + if (trigger & evt_urgent) + handle->events |= EPOLLPRI; + +#ifdef EPOLLRDHUP + if (triggers & evt_close) + handle->events |= EPOLLRDHUP; +#endif +} + +static short get_poll_events(struct epoll_event* handle) +{ + short trig = handle->events; + short evt = 0; + + if (trig & EPOLLIN) + evt |= evt_read; + + if (trig & EPOLLPRI) + evt |= evt_urgent; + + if (trig & EPOLLOUT) + evt |= evt_write; + + if (trig & EPOLLHUP) + evt |= evt_close; + + if (trig & EPOLLERR) + evt |= evt_error; + +#ifdef EPOLLRDHUP + if (trig & EPOLLRDHUP) + evt |= evt_close; +#endif + + return evt; +} + + +int net_initialize(int capacity) +{ + int i; + max_connections = capacity; + num_connections = 0; + epfd = epoll_create(max_connections); + if (epfd == -1) + { + hub_log(log_error, "net_initialize(): epoll_create failed"); + return -1; + } + + events = hub_malloc_zero(sizeof(struct epoll_event) * max_connections); + if (!events) + { + hub_log(log_error, "net_initialize(): hub_malloc failed"); + return -1; + } + + monitor_allocate((size_t) capacity); + + + +#ifdef DEBUG_EPOLL + dump_listeners(); +#endif + + net_stats_initialize(); + + return 0; +} + + +int net_shutdown() +{ + hub_log(log_trace, "Shutting down network monitor"); + if (epfd != -1) + { + close(epfd); + } + + hub_free(events); + hub_free(listeners); + return 0; +} + +#ifdef DEBUG_EPOLL +uint64_t get_time_difference_in_msec(struct timeval before, struct timeval after) +{ + uint64_t seconds = (after.tv_sec - before.tv_sec); + uint64_t out = seconds*1000; + if (seconds > 0) + out += ((after.tv_usec / 1000) + (1000 - (before.tv_usec / 1000))); + else + out += ((after.tv_usec - before.tv_usec) / 1000); + return out; +} +#endif + +int net_wait(int timeout_ms) +{ + int fired, n, max, ret; + struct net_event_listener* listener; + +#ifdef DEBUG_EPOLL + struct timeval tm_before; + struct timeval tm_after; + gettimeofday(&tm_before, NULL); + dump_listeners(); +#endif + + fired = epoll_wait(epfd, events, num_connections, timeout_ms); + if (fired == -1) { + if (errno != EINTR) + { + hub_log(log_error, "net_wait(): epoll_wait failed"); + } + return -1; + } + + for (n = 0; n < fired; n++) + { + listener = (struct net_event_listener*) events[n].data.ptr; + listener->revents = get_poll_events(&events[n]); + hub_log(log_dump, "net_wait(): epoll event detected (fd=%d, evt=%d, ptr=%p)", listener->fd, listener->revents, listener); + } + + max = num_connections; + + for (n = 0; n < max; n++) + { + listener = &listeners[n]; + if (listener && listener->fd != -1 && listener->revents) + { + hub_log(log_dump, "net_wait(): epoll trigger call (fd=%d, evt=%d, ptr=%p)", listener->fd, listener->revents, listener); + ret = listener->handler(listener); + listener->revents = 0; + } +#ifdef DEBUG_EPOLL + else + { + if (listener) + hub_log(log_dump, "net_wait(): epoll trigger ignore (fd=%d, evt=%d, ptr=%p)", listener->fd, listener->revents, listener); + } +#endif + } + +#ifdef DEBUG_EPOLL + gettimeofday(&tm_after, NULL); + size_t diff = (size_t) get_time_difference_in_msec(tm_before, tm_after); + dump_listeners(); + hub_log(log_debug, "net_wait(): time=%dms, triggered=%d", diff, fired); +#endif + + return 0; +} + + +int net_add(int fd, short events, void* data, net_event_handler_t handler) +{ + struct epoll_event ev; + struct net_event_listener* listener = monitor_get_free_listener(); + + hub_log(log_trace, "net_add(): adding socket (fd=%d, pos=%d)", fd, pos); + + if (!listener) + { + hub_log(log_error, "net_add(): unable to poll more sockets"); + return -1; + } + + net_event_listener_set(listener, fd, events, data, handler); + + set_poll_events(&ev, events); + ev.data.ptr = listener; + + if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) < 0) + { + hub_log(log_error, "net_add(): epoll_ctl error while adding socket (fd=%d)", fd); + net_event_listener_clear(listener); + return -1; + } + + num_connections++; + +#ifdef DEBUG_EPOLL + dump_listeners(); +#endif + return 0; +} + +int net_modify(int fd, short events) +{ + struct epoll_event ev; + struct net_event_listener* listener = monitor_get_listener(fd); + hub_log(log_trace, "net_modify(): modifying socket events (fd=%d)", fd); + + if (!listener) + { + hub_log(log_error, "net_modify(): unable to find socket."); + return -1; + } + + listener->events = events; + set_poll_events(&ev, events); + ev.data.ptr = listener; + + if (epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev) < 0) + { + hub_log(log_error, "net_add(): epoll_ctl error while modifying socket (fd=%d)", fd); + return -1; + } + +#ifdef DEBUG_EPOLL + dump_listeners(); +#endif + + return 0; +} + + +int net_remove(int fd) +{ + struct epoll_event ev; + struct net_event_listener* listener = monitor_get_listener(fd); + + hub_log(log_trace, "net_remove(): removing socket (fd=%d, pos=%d)", fd, pos); + + if (!listener) + { + /* The socket is not being monitored */ + hub_log(log_error, "net_remove(): unable to remove socket (fd=%d)", fd); + return -1; + } + + net_event_listener_clear(listener); + + if (epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &ev) < 0) + { + hub_log(log_error, "net_remove(): epoll_ctl error while removing socket (fd=%d)", fd); + return -1; + } + num_connections--; + +#ifdef DEBUG_EPOLL + dump_listeners(); +#endif + + return 0; +} + + +#endif /* HAVE_EPOLL */ + + + diff --git a/src/network-kqueue.c b/src/network-kqueue.c new file mode 100644 index 0000000..e5fdd88 --- /dev/null +++ b/src/network-kqueue.c @@ -0,0 +1,290 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#include "uhub.h" + +#ifdef HAVE_KQUEUE + +static struct kevent* events = 0; +static struct kevent* change = 0; +static int kfd = -1; + + +static void set_poll_events(struct kevent* handle, short trigger) +{ + if (!handle) { + hub_log(log_error, "OOOPS!!"); + return; + } + + memset(handle, 0, sizeof(struct kevent)); + + if (trigger & evt_accept || trigger & evt_read || trigger & evt_close) + handle->filter |= EVFILT_READ; + + if (trigger & evt_write /* || trigger & evt_accept*/) + handle->filter |= EVFILT_WRITE; +} + +static short get_poll_events(struct kevent* handle) +{ + short trig = handle->flags; + short evt = 0; + + if (trig & EVFILT_READ) + evt |= evt_read; + + if (trig & EVFILT_WRITE) + evt |= evt_write; + + if (trig & EV_EOF) + { + evt |= evt_close; + + if (handle->fflags) + evt |= evt_error; + } + + if (handle->filter == -1) + { + + evt |= evt_error; + } + + if (handle->data) + { + evt |= evt_accept; + } + + if (evt) + { + hub_log(log_error, "Evt: fd=%d, filter=%d, flags=%d, fflags=%d, data=%d evt=%#x", handle->ident, handle->filter, handle->flags, handle->fflags, (int) handle->data, evt); + + + } + + + return evt; +} + +int net_initialize(int capacity) +{ + int i; + max_connections = capacity; + num_connections = 0; + kfd = kqueue(); + if (kfd == -1) + { + hub_log(log_error, "net_initialize(): kqueue failed"); + return -1; + } + + events = (void*) hub_malloc_zero(sizeof(struct kevent) * max_connections); + if (!events) + { + hub_log(log_error, "net_initialize(): hub_malloc failed"); + return -1; + } + + change = (void*) hub_malloc_zero(sizeof(struct kevent) * max_connections); + if (!events) + { + hub_log(log_error, "net_initialize(): hub_malloc failed"); + hub_free(events); + return -1; + } + + + listeners = (void*) hub_malloc_zero(sizeof(struct net_event_listener) * max_connections); + if (!listeners) + { + hub_log(log_error, "net_initialize(): hub_malloc failed"); + hub_free(change); + hub_free(events); + return -1; + } + + for (i = 0; i < max_connections; i++) + { + listeners[i].fd = -1; + } + + net_stats_initialize(); + + return 0; +} + + +int net_shutdown() +{ + if (kfd != -1) { + return close(kfd); + } + + hub_free(events); + hub_free(change); + hub_free(listeners); + return 0; +} + + +int net_wait(int timeout_ms) +{ + int fired, n, max, ret; + struct net_event_listener* listener; + struct timespec timeout = { (timeout_ms / 1000), (timeout_ms % 1000) * 1000 }; + + fired = kevent(kfd, events, num_connections, change, num_connections, &timeout); + if (fired == -1) { + if (errno != EINTR) + { + hub_log(log_error, "net_wait(): kevent failed"); + } + return -1; + } + + for (n = 0; n < fired; n++) + { + listener = (struct net_event_listener*) events[n].udata; + if (listener) + { + listener->revents = get_poll_events(&events[n]); + hub_log(log_dump, "net_wait(): kqueue event detected (fd=%d, evt=%d, ptr=%p)", listener->fd, listener->revents, listener); + } + } + + max = num_connections; + for (n = 0; n < max; n++) + { + listener = &listeners[n]; + if (listener && listener->fd != -1 && listener->revents != 0) + { + hub_log(log_dump, "net_wait(): kqueue trigger call (fd=%d, evt=%d, ptr=%p)", listener->fd, listener->revents, listener); + ret = listener->handler(listener); + listener->revents = 0; + } + } + + return 0; +} + + +int net_add(int fd, short events_, void* data, net_event_handler_t handler) +{ + struct kevent* event; + struct net_event_listener* listener = monitor_get_listener(fd); + + hub_log(log_trace, "net_add(): adding socket (fd=%d)", fd); + + if (listener) + { + /* Already added! */ + return -1; + } + + listener = monitor_get_free_listener(); + if (!listener) + { + hub_log(log_error, "net_add(): unable to poll more sockets"); + return -1; + } + + net_event_listener_set(listener, fd, events_, data, handler); + + event = &events[pos]; + set_poll_events(event, events_); + event->ident = fd; + + event->flags |= EV_ADD; + event->flags |= EV_ONESHOT; + +#ifdef __APPLE__ + event->flags |= EV_ENABLE; +#endif + event->udata = listener; + + num_connections++; + return 0; +} + +int net_modify(int fd, short events_) +{ + struct kevent* event; + struct net_event_listener* listener = monitor_get_listener(fd); + + hub_log(log_trace, "net_modify(): modifying socket (fd=%d)", fd); + + if (!listener) + { + /* The socket is not being monitored */ + hub_log(log_error, "net_modify(): unable to find socket (fd=%d)", fd); + return -1; + } + + event = &events[pos]; + // set_poll_events(event, events_); + + event->ident = fd; + event->flags |= EV_ADD; + event->flags |= EV_ONESHOT; +#ifdef __APPLE__ + event->flags |= EV_ENABLE; +#endif + return 0; +} + + +int net_remove(int fd) +{ + struct kevent* event; + struct net_event_listener* listener = monitor_get_listener(fd); + + hub_log(log_trace, "net_remove(): removing socket (fd=%d)", fd); + + if (!listener) + { + /* The socket is not being monitored */ + hub_log(log_error, "net_remove(): unable to remove socket (fd=%d)", fd); + return -1; + } + + net_event_listener_clear(listener); + + event = &events[pos]; + event->ident = fd; + event->filter = 0; + event->flags = EV_DELETE; + +#ifdef __APPLE__ + event->flasg |= EV_DISABLE; +#endif + + event->fflags = 0; + event->data = 0; + event->udata = 0; + + num_connections--; + return 0; +} + + +#endif /* HAVE_KQUEUE */ + + + diff --git a/src/network.c b/src/network.c new file mode 100644 index 0000000..064f371 --- /dev/null +++ b/src/network.c @@ -0,0 +1,656 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#include "uhub.h" + +static int is_ipv6_supported = -1; /* -1 = CHECK, 0 = NO, 1 = YES */ +static int net_initialized = 0; +static struct net_statistics stats; +static struct net_statistics stats_total; +static struct event_base* evbase; + +#if defined(IPV6_BINDV6ONLY) +#define SOCK_DUAL_STACK_OPT IPV6_BINDV6ONLY +#elif defined(IPV6_V6ONLY) +#define SOCK_DUAL_STACK_OPT IPV6_V6ONLY +#endif + + +int net_initialize() +{ + if (!net_initialized) + { + +#ifdef WINSOCK + struct WSAData wsa; + if (WSAStartup(MAKEWORD(2, 2), &wsa) != NO_ERROR) + { + hub_log(log_error, "Unable to initialize winsock."); + return -1; + } +#endif /* WINSOCK */ + + hub_log(log_trace, "Initializing network monitor."); + net_stats_initialize(); + +#ifdef SSL_SUPPORT + /* FIXME: Initialize OpenSSL here. */ +#endif /* SSL_SUPPORT */ + +#ifdef OLD_LIBEVENT + event_init(); +#else + evbase = event_init(); + if (!evbase) + { + hub_log(log_error, "Unable to initialize libevent."); + return -1; + } +#endif + net_initialized = 1; + return 0; + } + return -1; +} + + +int net_shutdown() +{ + if (net_initialized) + { + hub_log(log_trace, "Shutting down network monitor"); + +#ifdef SSL_SUPPORT + /* FIXME: Shutdown OpenSSL here. */ +#endif + +#ifndef OLD_LIBEVENT + event_base_free(evbase); +#endif + evbase = 0; + +#ifdef WINSOCK + WSACleanup(); +#endif + net_initialized = 0; + return 0; + } + return -1; +} + + +int net_error() +{ +#ifdef WINSOCK + return WSAGetLastError(); +#else + return errno; +#endif +} + + +const char* net_error_string(int code) +{ +#ifdef WINSOCK + static char string[32]; + snprintf(string, 32, "error code: %d", code); + return string; +#else + return strerror(code); +#endif +} + + +static int net_setsockopt(int fd, int level, int opt, const void* optval, socklen_t optlen) +{ +#ifdef WINSOCK + return setsockopt(fd, level, opt, (const char*) optval, optlen); +#else + return setsockopt(fd, level, opt, optval, optlen); +#endif +} + + +int net_set_nonblocking(int fd, int toggle) +{ + int ret; + +#ifdef NETAPI_DUMP + hub_log(log_dump, "net_set_nonblocking(): fd=%d", fd); +#endif + +#ifdef WINSOCK + u_long on = toggle ? 1 : 0; + ret = ioctlsocket(fd, FIONBIO, &on); +#else + ret = ioctl(fd, FIONBIO, &toggle); +#endif + if (ret == -1) + { + hub_log(log_error, "net_set_nonblocking(): ioctl failed (fd=%d): %s", fd, net_error_string(net_error())); + return -1; + } + return 0; +} + + +/* NOTE: Possibly only supported on BSD and OSX? */ +int net_set_nosigpipe(int fd, int toggle) +{ +#ifdef SO_NOSIGPIPE + int ret; +#ifdef NETAPI_DUMP + hub_log(log_dump, "net_set_nosigpipe(): fd=%d", fd); +#endif + ret = net_setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &toggle, sizeof(toggle)); + if (ret == -1) + { + hub_log(log_error, "net_set_linger(): setsockopt failed (fd=%d): %s", fd, net_error_string(net_error())); + return -1; + } +#endif + return 0; +} + +int net_set_close_on_exec(int fd, int toggle) +{ +#ifdef NETAPI_DUMP + hub_log(log_dump, "net_set_close_on_exec(): fd=%d", fd); +#endif +#ifdef WINSOCK + return -1; /* FIXME: How is this done on Windows? */ +#else + return fcntl(fd, F_SETFD, toggle); +#endif +} + + +int net_set_linger(int fd, int toggle) +{ + int ret; +#ifdef NETAPI_DUMP + hub_log(log_dump, "net_set_linger(): fd=%d", fd); +#endif + ret = net_setsockopt(fd, SOL_SOCKET, SO_LINGER, &toggle, sizeof(toggle)); + if (ret == -1) + { + hub_log(log_error, "net_set_linger(): setsockopt failed (fd=%d): %s", fd, net_error_string(net_error())); + return -1; + } + return 0; +} + + +int net_set_keepalive(int fd, int toggle) +{ + int ret; +#ifdef NETAPI_DUMP + hub_log(log_dump, "net_set_keepalive(): fd=%d", fd); +#endif + ret = net_setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &toggle, sizeof(toggle)); + if (ret == -1) + { + hub_log(log_error, "net_set_keepalive(): setsockopt failed (fd=%d): %s", fd, net_error_string(net_error())); + return -1; + } + return 0; +} + + +int net_set_reuseaddress(int fd, int toggle) +{ + int ret; +#ifdef NETAPI_DUMP + hub_log(log_dump, "net_set_reuseaddress(): fd=%d", fd); +#endif + ret = net_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &toggle, sizeof(toggle)); + if (ret == -1) + { + hub_log(log_error, "net_set_reuseaddress(): setsockopt failed (fd=%d): %s", fd, net_error_string(net_error())); + return -1; + } + return 0; +} + + +int net_close(int fd) +{ +#ifdef WINSOCK + int ret = closesocket(fd); +#else + int ret = close(fd); +#endif + + if (ret == 0) + { + net_stats_add_close(); + } + else + { + if (fd != -1) + { + net_stats_add_error(); + } + } + return ret; +} + + +int net_accept(int fd) +{ + struct sockaddr_storage addr; + socklen_t addr_size; + int ret = 0; + addr_size = sizeof(struct sockaddr_storage); + memset(&addr, 0, addr_size); + ret = accept(fd, (struct sockaddr*) &addr, &addr_size); + + if (ret == -1) + { + switch (net_error()) + { +#if defined(__linux__) + case ENETDOWN: + case EPROTO: + case ENOPROTOOPT: + case EHOSTDOWN: + case ENONET: + case EHOSTUNREACH: + case EOPNOTSUPP: + errno = EWOULDBLOCK; +#endif + case EWOULDBLOCK: + break; + default: + hub_log(log_error, "net_accept(): accept failed (fd=%d, errno=%d, msg=%s)", fd, net_error(), net_error_string(net_error())); + net_stats_add_error(); + return -1; + } + } + else + { + net_stats_add_accept(); + } + + return ret; +} + + +int net_connect(int fd, const struct sockaddr *serv_addr, socklen_t addrlen) +{ + int ret = connect(fd, serv_addr, addrlen); + if (ret == -1) + { + if (net_error() != EINPROGRESS) + { + hub_log(log_error, "net_connect(): connect failed (fd=%d, errno=%d, msg=%s)", fd, net_error(), net_error_string(net_error())); + net_stats_add_error(); + } + } + return ret; +} + + + +int net_is_ipv6_supported() +{ + if (is_ipv6_supported == -1) + { + int ret = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (ret == -1) + { +#ifdef WINSOCK + if (net_error() == WSAEAFNOSUPPORT) +#else + if (net_error() == EAFNOSUPPORT) +#endif + { + hub_log(log_trace, "net_is_ipv6_supported(): IPv6 is not supported on this system."); + is_ipv6_supported = 0; + return 0; + } + + hub_log(log_error, "net_is_ipv6_supported(): Unknown error (errno=%d, msg=%s)", net_error(), net_error_string(net_error())); + + } + else + { +#ifdef SOCK_DUAL_STACK_OPT + int off = 0; + if (net_setsockopt(ret, IPPROTO_IPV6, SOCK_DUAL_STACK_OPT, (char*) &off, sizeof(off)) < 0) + { + hub_log(log_error, "net_socket_create(): Dual stack IPv6/IPv4 is not supported."); + is_ipv6_supported = 0; + } + else + { + is_ipv6_supported = 1; + } +#else + is_ipv6_supported = 0; +#endif + net_close(ret); + } + } + return is_ipv6_supported; +} + + +int net_socket_create(int af, int type, int protocol) +{ + int sd = socket(af, type, protocol); + if (sd == -1) + { + hub_log(log_error, "net_socket_create(): socket failed (errno=%d, msg=%s)", net_error(), net_error_string(net_error())); + } + +#ifdef SOCK_DUAL_STACK_OPT + /* BSD style */ + if (af == AF_INET6) + { + int off = 0; + if (net_setsockopt(sd, IPPROTO_IPV6, SOCK_DUAL_STACK_OPT, (char*) &off, sizeof(off)) < 0) + { + hub_log(log_error, "net_socket_create(): Cannot set socket to dual stack mode IPv6/IPv4 (%d - %s).", net_error(), net_error_string(net_error())); + } + } +#endif + + return sd; +} + +const char* net_address_to_string(int af, const void* src, char* dst, socklen_t cnt) +{ +#ifdef WINSOCK + struct sockaddr_in sin4; + struct sockaddr_in6 sin6; + struct in_addr* addr4 = (struct in_addr*) src; + struct in6_addr* addr6 = (struct in6_addr*) src; + size_t size; + LPSOCKADDR addr; + DWORD len = cnt; + + switch (af) + { + case AF_INET: + sin4.sin_family = AF_INET; + sin4.sin_port = 0; + sin4.sin_addr = *addr4; + size = sizeof(sin4); + addr = (LPSOCKADDR) &sin4; + break; + + case AF_INET6: + sin6.sin6_family = AF_INET6; + sin6.sin6_port = 0; + sin6.sin6_addr = *addr6; + size = sizeof(sin6); + addr = (LPSOCKADDR) &sin6; + break; + + default: + return NULL; + } + + if (WSAAddressToString(addr, size, NULL, dst, &len) == 0) + { + return dst; + } + + return NULL; +#else + return inet_ntop(af, src, dst, cnt); +#endif +} + +int net_string_to_address(int af, const char* src, void* dst) +{ +#ifdef WINSOCK + int ret, size; + struct sockaddr_in addr4; + struct sockaddr_in6 addr6; + struct sockaddr* addr = 0; + if (af == AF_INET6) + { + if (net_is_ipv6_supported() != 1) return -1; + size = sizeof(struct sockaddr_in6); + addr = (struct sockaddr*) &addr6; + } + else + { + size = sizeof(struct sockaddr_in); + addr = (struct sockaddr*) &addr4; + } + + if (!net_initialized) + net_initialize(); + + ret = WSAStringToAddressA((char*) src, af, NULL, addr, &size); + if (ret == -1) + { + return -1; + } + + if (af == AF_INET6) + { + memcpy(dst, &addr6.sin6_addr, sizeof(addr6.sin6_addr)); + } + else + { + memcpy(dst, &addr4.sin_addr, sizeof(addr4.sin_addr)); + } + + return 1; +#else + return inet_pton(af, src, dst); +#endif +} + + + + +const char* net_get_peer_address(int fd) +{ + static char address[INET6_ADDRSTRLEN+1]; + struct sockaddr_storage storage; + struct sockaddr_in6* name6; + struct sockaddr_in* name4; + struct sockaddr* name; + + memset(address, 0, INET6_ADDRSTRLEN); + socklen_t namelen = sizeof(struct sockaddr_storage); + memset(&storage, 0, namelen); + + name6 = (struct sockaddr_in6*) &storage; + name4 = (struct sockaddr_in*) &storage; + name = (struct sockaddr*) &storage; + + + int af = net_is_ipv6_supported() ? AF_INET6 : AF_INET; + + if (getpeername(fd, (struct sockaddr*) name, &namelen) != -1) + { + if (af == AF_INET6) + { + net_address_to_string(af, (void*) &name6->sin6_addr, address, INET6_ADDRSTRLEN); + if (strncmp(address, "::ffff:", 7) == 0) /* IPv6 mapped IPv4 address. */ + { + return &address[7]; + } + hub_log(log_trace, "net_get_peer_address(): address=%s", address); + return address; + } + else + { + net_address_to_string(af, (void*) &name4->sin_addr, address, INET6_ADDRSTRLEN); + hub_log(log_trace, "net_get_peer_address(): address=%s", address); + return address; + } + } + else + { + hub_log(log_error, "net_get_peer_address(): getsockname failed (fd=%d, errno=%d, msg=%s)", fd, net_error(), net_error_string(net_error())); + } + + return "0.0.0.0"; +} + + +ssize_t net_recv(int fd, void* buf, size_t len, int flags) +{ + ssize_t ret = recv(fd, buf, len, flags); + if (ret >= 0) + { + net_stats_add_rx(ret); + } + else + { + if (net_error() != EWOULDBLOCK) + { + hub_log(log_debug, "net_recv(): failed (fd=%d, errno=%d, msg=%s)", fd, net_error(), net_error_string(net_error())); + net_stats_add_error(); + } + } + return ret; +} + + +ssize_t net_send(int fd, void* buf, size_t len, int flags) +{ + ssize_t ret = send(fd, buf, len, flags); + if (ret >= 0) + { + net_stats_add_tx(ret); + } + else + { + if (net_error() != EWOULDBLOCK) + { + hub_log(log_debug, "net_send(): failed (fd=%d, errno=%d, msg=%s)", fd, net_error(), net_error_string(net_error())); + net_stats_add_error(); + } + } + return ret; +} + + +int net_bind(int fd, const struct sockaddr *my_addr, socklen_t addrlen) +{ + int ret = bind(fd, my_addr, addrlen); + if (ret == -1) + { + hub_log(log_error, "net_bind(): failed (fd=%d, errno=%d, msg=%s)", fd, net_error(), net_error_string(net_error())); + net_stats_add_error(); + } + return ret; +} + + +int net_listen(int fd, int backlog) +{ + int ret = listen(fd, backlog); + if (ret == -1) + { + hub_log(log_error, "net_listen(): failed (fd=%d, errno=%d, msg=%s)", fd, net_error(), net_error_string(net_error())); + net_stats_add_error(); + } + return ret; +} + + +void net_stats_initialize() +{ + memset(&stats_total, 0, sizeof(struct net_statistics)); + stats_total.timestamp = time(NULL); + + memset(&stats, 0, sizeof(struct net_statistics)); + stats.timestamp = time(NULL); +} + + +void net_stats_get(struct net_statistics** intermediate, struct net_statistics** total) +{ + *intermediate = &stats; + *total = &stats_total; +} + + +void net_stats_report() +{ + int factor = (time(NULL) - stats.timestamp); + if (!factor) factor++; + + hub_log(log_info, "Statistics NET: tx=%d KB/s, rx=%d KB/s, (acc=%d/cls=%d/err=%d)", + (int) ((stats.tx / factor) / 1024), + (int) ((stats.rx / factor) / 1024), + (int) stats.accept, + (int) stats.closed, + (int) stats.errors); +} + +void net_stats_reset() +{ + stats_total.tx += stats.tx; + stats_total.rx += stats.rx; + stats_total.accept += stats.accept; + stats_total.errors += stats.errors; + stats_total.closed += stats.closed; + + memset(&stats, 0, sizeof(struct net_statistics)); + stats.timestamp = time(NULL); +} + + +int net_stats_timeout() +{ + /* FIXME: Configurable time for dumping network statistics */ + return (time(NULL) - stats.timestamp > 60) ? 1 : 0; +} + + +void net_stats_add_tx(size_t bytes) +{ + stats.tx += bytes; +} + + +void net_stats_add_rx(size_t bytes) +{ + stats.rx += bytes; +} + + +void net_stats_add_accept() +{ + stats.accept++; +} + + +void net_stats_add_error() +{ + stats.errors++; +} + + +void net_stats_add_close() +{ + stats.closed++; +} + + diff --git a/src/network.h b/src/network.h new file mode 100644 index 0000000..e195b12 --- /dev/null +++ b/src/network.h @@ -0,0 +1,256 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#ifndef HAVE_UHUB_NETWORK_H +#define HAVE_UHUB_NETWORK_H + +struct net_statistics +{ + time_t timestamp; + size_t tx; + size_t rx; + size_t accept; + size_t closed; + size_t errors; +}; + +/** + * Initialize the socket monitor subsystem. + * On some operating systems this will also involve loading the TCP/IP stack + * (needed on Windows at least). + * + * @param max_connections The maximum number of sockets the monitor can handle. + * @return -1 on error, 0 on success + */ +extern int net_initialize(); + +/** + * Shutdown the socket monitor. + * On some operating systems this will also ensure the TCP/IP stack + * is loaded. + * + * @return -1 on error, 0 on success + */ +extern int net_shutdown(); + +/** + * @return the number of sockets currrently being monitored. + */ +extern int net_monitor_count(); + +/** + * @return the monitor's socket capacity. + */ +extern int net_monitor_capacity(); + +/** + * @return the last error code occured. + * + * NOTE: On Windows this is the last error code from the socket library, but + * on UNIX this is the errno variable that can be overwritten by any + * libc function. + * For this reason, only rely on net_error() immediately after a + * socket function call. + */ +extern int net_error(); +extern const char* net_error_string(int code); + +/** + * A wrapper for the socket() function call. + */ +extern int net_socket_create(int af, int type, int protocol); + +/** + * A wrapper for the close() function call. + */ +extern int net_close(int fd); + +/** + * A wrapper for the accept() function call. + */ +extern int net_accept(int fd); + +/** + * A wrapper for the connect() call. + */ +extern int net_connect(int fd, const struct sockaddr *serv_addr, socklen_t addrlen); + +/** + * A wrapper for the bind() function call. + */ +extern int net_bind(int fd, const struct sockaddr *my_addr, socklen_t addrlen); + +/** + * A wrapper for the listen() function call. + */ +extern int net_listen(int sockfd, int backlog); + +/** + * This will set the socket to blocking or nonblocking mode. + * @param fd socket descriptor + * @param toggle if non-zero nonblocking mode, otherwise blocking mode is assumed + * @return -1 on error, 0 on success + */ +extern int net_set_nonblocking(int fd, int toggle); + +/** + * This will prevent the socket to generate a SIGPIPE in case the socket goes down. + * NOTE: Not all operating systems support this feature. In that case this will return success value. + * + * @param fd socket descriptor + * @param toggle if non-zero ignore sigpipe, otherwise disable it. + * @return -1 on error, 0 on success + */ +extern int net_set_nosigpipe(int fd, int toggle); + +/** + * This will set the close-on-exec flag. This means if any subprocess is + * started any open file descriptors or sockets will not be inherited if this + * is turned on. Otherwise, subprocesses invoked via exec() can read/write + * to these sockets. + * + * @param fd socket descriptor + * @param toggle if non-zero close-on-exec is enabled, otherwise disabled. + * @return -1 on error, 0 on success. + */ +extern int net_set_close_on_exec(int fd, int toggle); + +/** + * Enable/disable linger on close if data is present. + * + * @param fd socket descriptor + * @param toggle enable if non-zero + * @return -1 on error, 0 on success. + */ +extern int net_set_linger(int fd, int toggle); + +/** + * This will set or unset the SO_REUSEADDR flag. + * @param fd socket descriptor + * @param toggle Set SO_REUSEADDR if non-zero, otherwise unset it. + * @return -1 on error, 0 on success + */ +extern int net_set_reuseaddress(int fd, int toggle); + +/** + * A wrapper for the recv() function call. + */ +extern ssize_t net_recv(int fd, void* buf, size_t len, int flags); + +/** + * A wrapper for the send() function call. + */ +extern ssize_t net_send(int fd, void* buf, size_t len, int flags); + +/** + * This tries to create a AF_INET6 socket. + * If it succeeds it concludes IPv6 is supported on the host operating + * system. If the call fails with EAFNOSUPPORT the host system + * does not support IPv6. + * The result is cached so further calls to this function are cheap. + */ +extern int net_is_ipv6_supported(); + +/** + * This will return a string containing the peer IP-address of + * the connected peer associated with the given socket. + * + * @param fd socket descriptor + * @return IP address (IPv6 or IPv4), or "0.0.0.0" if unable to determine the address. + */ +extern const char* net_get_peer_address(int fd); + +/** + * See man(3) inet_ntop. + */ +extern const char* net_address_to_string(int af, const void *src, char *dst, socklen_t cnt); + +/** + * See man(3) inet_pton. + */ +extern int net_string_to_address(int af, const char *src, void *dst); + + +/** + * Network statistics monitor. + * + * Keeps track of bandwidth usage, sockets accepted, closed, + * errors etc. + */ +extern void net_stats_initialize(); +extern void net_stats_report(); +extern void net_stats_reset(); +extern void net_stats_add_tx(size_t bytes); +extern void net_stats_add_rx(size_t bytes); +extern void net_stats_add_accept(); +extern void net_stats_add_error(); +extern void net_stats_add_close(); +extern int net_stats_timeout(); +extern void net_stats_get(struct net_statistics** intermediate, struct net_statistics** total); + + +#if defined(WINSOCK) && !defined(__CYGWIN__) + +// #define EINTR WSAEINTR +// #define EACCES WSAEACCES +// #define EFAULT WSAEFAULT +// #define EINVAL WSAEINVAL +// #define EMFILE WSAEMFILE +#define EWOULDBLOCK WSAEWOULDBLOCK +#define EINPROGRESS WSAEINPROGRESS +#define EALREADY WSAEALREADY +#define ENOTSOCK WSAENOTSOCK +#define EDESTADDRREQ WSAEDESTADDRREQ +#define EMSGSIZE WSAEMSGSIZE +#define EPROTOTYPE WSAEPROTOTYPE +#define ENOPROTOOPT WSAENOPROTOOPT +#define EPROTONOSUPPORT WSAEPROTONOSUPPORT +#define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT +#define EOPNOTSUPP WSAEOPNOTSUPP +#define EPFNOSUPPORT WSAEPFNOSUPPORT +#define EAFNOSUPPORT WSAEAFNOSUPPORT +#define EADDRINUSE WSAEADDRINUSE +#define EADDRNOTAVAIL WSAEADDRNOTAVAIL +#define ENETDOWN WSAENETDOWN +#define ENETUNREACH WSAENETUNREACH +#define ENETRESET WSAENETRESET +#define ECONNABORTED WSAECONNABORTED +#define ECONNRESET WSAECONNRESET +#define ENOBUFS WSAENOBUFS +#define EISCONN WSAEISCONN +#define ENOTCONN WSAENOTCONN +#define ESHUTDOWN WSAESHUTDOWN +#define ETOOMANYREFS WSAETOOMANYREFS +#define ETIMEDOUT WSAETIMEDOUT +#define ECONNREFUSED WSAECONNREFUSED +#define ELOOP WSAELOOP +// #define ENAMETOOLONG WSAENAMETOOLONG +#define EHOSTDOWN WSAEHOSTDOWN +#define EHOSTUNREACH WSAEHOSTUNREACH +// #define ENOTEMPTY WSAENOTEMPTY +#define EPROCLIM WSAEPROCLIM +#define EUSERS WSAEUSERS +#define EDQUOT WSAEDQUOT +#define ESTALE WSAESTALE +#define EREMOTE WSAEREMOTE + +#endif /* WINSOCK && !__CYGWIN__ */ + + +#endif /* HAVE_UHUB_NETWORK_H */ diff --git a/src/plugin.h b/src/plugin.h new file mode 100644 index 0000000..238c5e4 --- /dev/null +++ b/src/plugin.h @@ -0,0 +1,52 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +typedef void (*plugin_event_startup)(struct hub*); +typedef void (*plugin_event_shutdown)(struct hub*); +typedef void (*plugin_event_user_login)(struct hub*, struct user*); +typedef void (*plugin_event_user_logout)(struct hub*, struct user*); +typedef int (*plugin_event_connect)(struct hub*, struct ip_addr_encap); +typedef void (*plugin_event_disconnect)(struct hub*, struct user*); +typedef int (*plugin_event_message)(struct hub*, struct user*, struct adc_message*); +typedef void (*plugin_event_support)(struct hub*, struct user*, int); + +struct uhub_plugin +{ + /** Starting the hub */ + plugin_event_startup evt_startup; + + /** Shutting down the hub */ + plugin_event_shutdown evt_shutdown; + + /** Someone connected to the hub (we only have IP at this point). */ + plugin_event_connect evt_connect; + + /** Someone disconnected from the hub (but was not successfully logged in). */ + plugin_event_disconnect evt_disconnect; + + /** A client sent a message about which protocol extensions it supports */ + plugin_event_support evt_support; + + /** A client was successfully logged in to the hub */ + plugin_event_user_login evt_login; + + /** A client (previously logged in) has disconnected. */ + plugin_event_user_logout evt_logout; +}; + diff --git a/src/route.c b/src/route.c new file mode 100644 index 0000000..92e0d1d --- /dev/null +++ b/src/route.c @@ -0,0 +1,237 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#include "uhub.h" + + +int route_message(struct user* u, struct adc_message* msg) +{ + struct user* target = NULL; + + switch (msg->cache[0]) + { + case 'B': /* Broadcast to all logged in clients */ + route_to_all(u->hub, msg); + break; + + case 'D': + target = get_user_by_sid(u->hub, msg->target); + if (target) + { + route_to_user(target, msg); + } + break; + + case 'E': + target = get_user_by_sid(u->hub, msg->target); + if (target) + { + route_to_user(target, msg); + route_to_user(u, msg); + } + break; + + case 'F': + route_to_subscribers(u->hub, msg); + break; + + default: + /* Ignore the message */ + break; + } + return 0; +} + + +static void queue_command(struct user* user, struct adc_message* msg__, int offset) +{ + struct adc_message* msg = adc_msg_incref(msg__); + list_append(user->send_queue, msg); + + hub_log(log_trace, "queue_command(), user=%p, msg=%p (%zu), offset=%d", user, msg, msg->references, offset); + + if (offset > 0) + { + user->send_queue_size += msg->length - offset; + user->send_queue_offset = offset; + user->tm_last_write = time(NULL); + } + else + { + user->send_queue_size += msg->length; + user->send_queue_offset = 0; + } +} + +// #define ALWAYS_QUEUE_MESSAGES + +int route_to_user(struct user* user, struct adc_message* msg) +{ + int ret; + +#if LOG_SEND_MESSAGES_WHEN_ROUTED + char* data = strndup(msg->cache, msg->length-1); + hub_log(log_protocol, "send %s: %s", sid_to_string(user->sid), data); + free(data); +#endif + +#ifndef ALWAYS_QUEUE_MESSAGES + if (user->send_queue_size == 0 && !user_is_disconnecting(user)) + { + ret = net_send(user->sd, msg->cache, msg->length, UHUB_SEND_SIGNAL); + + if (ret == msg->length) + { + return 1; + } + + if (ret >= 0 || (ret == -1 && net_error() == EWOULDBLOCK)) + { + queue_command(user, msg, ret); + + if (user->send_queue_size && user->ev_write) + event_add(user->ev_write, NULL); + } + else + { + /* A socket error occured */ + user_disconnect(user, quit_socket_error); + return 0; + } + } + else +#endif + { + if (!user_flag_get(user, flag_user_list) && user->send_queue_size + msg->length > user->hub->config->max_send_buffer && msg->priority >= 0) + { + /* User is not able to swallow the data, let's cut our losses and disconnect. */ + user_disconnect(user, quit_send_queue); + return 0; + } + else + { + if (user->send_queue_size + msg->length > user->hub->config->max_send_buffer_soft && msg->priority >= 0) + { + /* Don't queue this message if it is low priority! */ + } + else + { + queue_command(user, msg, 0); + if (user->ev_write) + event_add(user->ev_write, NULL); + } + } + } + + return 1; +} + + +int route_to_all(struct hub_info* hub, struct adc_message* command) /* iterate users */ +{ + struct user* user = (struct user*) list_get_first(hub->users->list); + while (user) + { + route_to_user(user, command); + user = (struct user*) list_get_next(hub->users->list); + } + + return 0; +} + + +int route_to_subscribers(struct hub_info* hub, struct adc_message* command) /* iterate users */ +{ + int do_send; + char* tmp; + + struct user* user = (struct user*) list_get_first(hub->users->list); + while (user) + { + if (user->feature_cast) + { + do_send = 1; + + tmp = list_get_first(command->feature_cast_include); + while (tmp) + { + if (!user_have_feature_cast_support(user, tmp)) + { + do_send = 0; + break; + } + tmp = list_get_next(command->feature_cast_include);; + } + + if (!do_send) { + user = (struct user*) list_get_next(hub->users->list); + continue; + } + + tmp = list_get_first(command->feature_cast_exclude); + while (tmp) + { + if (user_have_feature_cast_support(user, tmp)) + { + do_send = 0; + break; + } + tmp = list_get_next(command->feature_cast_exclude); + } + + if (do_send) + { + route_to_user(user, command); + } + } + user = (struct user*) list_get_next(hub->users->list); + } + + return 0; +} + +int route_info_message(struct user* u) +{ + if (!user_is_nat_override(u)) + { + return route_to_all(u->hub, u->info); + } + else + { + struct adc_message* cmd = adc_msg_copy(u->info); + const char* address = (char*) net_get_peer_address(u->sd); + struct user* user = 0; + + adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV4_ADDR); + adc_msg_add_named_argument(cmd, ADC_INF_FLAG_IPV4_ADDR, address); + + user = (struct user*) list_get_first(u->hub->users->list); + while (user) + { + if (user_is_nat_override(user)) + route_to_user(user, cmd); + else + route_to_user(user, u->info); + + user = (struct user*) list_get_next(u->hub->users->list); + } + adc_msg_free(cmd); + } + return 0; +} diff --git a/src/route.h b/src/route.h new file mode 100644 index 0000000..6e69e8d --- /dev/null +++ b/src/route.h @@ -0,0 +1,51 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#ifndef HAVE_UHUB_ROUTE_H +#define HAVE_UHUB_ROUTE_H + +/** + * Route a message by sending it to it's final destination. + */ +extern int route_message(struct user* u, struct adc_message* msg); + +/** + * Transmit message directly to one user. + */ +extern int route_to_user(struct user*, struct adc_message* command); + +/** + * Broadcast message to all users. + */ +extern int route_to_all(struct hub_info* hub, struct adc_message* command); + +/** + * Broadcast message to all users subscribing to the type of message. + */ +extern int route_to_subscribers(struct hub_info* hub, struct adc_message* command); + +/** + * Broadcast initial info message to all users. + * This will ensure the correct IP is seen by other users + * in case nat override is in use. + */ +extern int route_info_message(struct user* user); + + +#endif /* HAVE_UHUB_ROUTE_H */ diff --git a/src/sid.c b/src/sid.c new file mode 100644 index 0000000..135891f --- /dev/null +++ b/src/sid.c @@ -0,0 +1,61 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#include "uhub.h" + +const char* BASE32_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + +char* sid_to_string(sid_t sid_) +{ + static char t_sid[5]; + sid_t sid = (sid_ & 0xFFFFF); /* 20 bits only */ + sid_t A, B, C, D = 0; + D = (sid % 32); + sid = (sid - D) / 32; + C = (sid % 32); + sid = (sid - C) / 32; + B = (sid % 32); + sid = (sid - B) / 32; + A = (sid % 32); + t_sid[0] = BASE32_ALPHABET[A]; + t_sid[1] = BASE32_ALPHABET[B]; + t_sid[2] = BASE32_ALPHABET[C]; + t_sid[3] = BASE32_ALPHABET[D]; + t_sid[4] = 0; + return t_sid; +} + + +sid_t string_to_sid(const char* sid) +{ + sid_t nsid = 0; + sid_t n, x; + sid_t factors[] = { 32768, 1024, 32, 1}; + + if (!sid || strlen(sid) != 4) return 0; + + for (n = 0; n < 4; n++) { + for (x = 0; x < strlen(BASE32_ALPHABET); x++) + if (sid[n] == BASE32_ALPHABET[x]) break; + if (x == 32) return 0; + nsid += x * factors[n]; + } + return nsid; +} + diff --git a/src/sid.h b/src/sid.h new file mode 100644 index 0000000..6c43f86 --- /dev/null +++ b/src/sid.h @@ -0,0 +1,67 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#ifndef HAVE_UHUB_SID_H +#define HAVE_UHUB_SID_H + +#define SID_MAX 1048576 + +extern const char* BASE32_ALPHABET; +extern char* sid_to_string(sid_t sid_); +extern sid_t string_to_sid(const char* sid); + +struct sid_map +{ + struct user* ptr; + struct sid_map* next; +}; + +/** + * Session IDs are heavily reused, since they are a fairly scarce + * resource. Only one (2^10)-1 exist, since it is a four byte base32-encoded + * value and 'AAAA' (0) is reserved for the hub. + * + * Initialize with sid_initialize(), which sets min and max to one, and count to 0. + * + * When allocating a session ID: + * - If 'count' is less than the pool size (max-min), then allocate within the pool + * - Increase the pool size (see below) + * - If unable to do that, hub is really full - don't let anyone in! + * + * When freeing a session ID: + * - If the session ID being freed is 'max', then decrease the pool size by one. + * + */ +struct sid_pool +{ + sid_t min; + sid_t max; + sid_t count; + struct sid_map* map; +}; + + +extern void sid_initialize(struct sid_pool*); +extern sid_t sid_alloc(struct sid_pool*, struct user*); +extern void sid_free(struct sid_pool*, sid_t); + + + +#endif /* HAVE_UHUB_SID_H */ + diff --git a/src/tiger.c b/src/tiger.c new file mode 100644 index 0000000..db82d2e --- /dev/null +++ b/src/tiger.c @@ -0,0 +1,1241 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#include "uhub.h" + +#define PASSES 3 + +extern uint64_t tiger_sboxes[4*256]; + +#define t1 (tiger_sboxes) +#define t2 (tiger_sboxes + 256) +#define t3 (tiger_sboxes + 512) +#define t4 (tiger_sboxes + 768) + +#define ROUND(a, b, c, x, mul) \ + c ^= x; \ + a -= t1[c & 0xFF] ^ t2[(c >> 0x10) & 0xFF] ^ \ + t3[(c >> 0x20) & 0xFF] ^ t4[(c >> 0x30) & 0xFF] ; \ + b += t4[(c >> 0x08) & 0xFF] ^ t3[(c >> 0x18) & 0xFF] ^ \ + t2[(c >> 0x28) & 0xFF] ^ t1[(c >> 0x38) & 0xFF] ; \ + b *= mul; + +#define PASS(a, b, c, mul) \ + ROUND(a, b, c, x0, mul) \ + ROUND(b, c, a, x1, mul) \ + ROUND(c, a, b, x2, mul) \ + ROUND(a, b, c, x3, mul) \ + ROUND(b, c, a, x4, mul) \ + ROUND(c, a, b, x5, mul) \ + ROUND(a, b, c, x6, mul) \ + ROUND(b, c, a, x7, mul) + +void tiger_compress(uint64_t* str, uint64_t state[3]) { + uint64_t a, b, c, swap; + uint64_t x0, x1, x2, x3, x4, x5, x6, x7; + uint64_t aa, bb, cc; + size_t pass_no; + a = state[0]; + b = state[1]; + c = state[2]; + + x0 = str[0]; + x1 = str[1]; + x2 = str[2]; + x3 = str[3]; + x4 = str[4]; + x5 = str[5]; + x6 = str[6]; + x7 = str[7]; + + aa = a; + bb = b; + cc = c; + + PASS(a, b, c, 5); + + x0 -= x7 ^ 0xA5A5A5A5A5A5A5A5ULL; + x1 ^= x0; + x2 += x1; + x3 -= x2 ^ ((~x1) << 19); + x4 ^= x3; + x5 += x4; + x6 -= x5 ^ ((~x4) >> 23); + x7 ^= x6; + x0 += x7; + x1 -= x0 ^ ((~x7) << 19); + x2 ^= x1; + x3 += x2; + x4 -= x3 ^ ((~x2) >> 23); + x5 ^= x4; + x6 += x5; + x7 -= x6 ^ 0x0123456789ABCDEFULL; + + PASS(c, a, b, 7); + + x0 -= x7 ^ 0xA5A5A5A5A5A5A5A5ULL; + x1 ^= x0; + x2 += x1; + x3 -= x2 ^ ((~x1) << 19); + x4 ^= x3; + x5 += x4; + x6 -= x5 ^ ((~x4) >> 23); + x7 ^= x6; + x0 += x7; + x1 -= x0 ^ ((~x7) << 19); + x2 ^= x1; + x3 += x2; + x4 -= x3 ^ ((~x2) >> 23); + x5 ^= x4; + x6 += x5; + x7 -= x6 ^ 0x0123456789ABCDEFULL; + + PASS(b, c, a, 9); + + for (pass_no = 3; pass_no < PASSES; pass_no++) + { + x0 -= x7 ^ 0xA5A5A5A5A5A5A5A5ULL; + x1 ^= x0; + x2 += x1; + x3 -= x2 ^ ((~x1) << 19); + x4 ^= x3; + x5 += x4; + x6 -= x5 ^ ((~x4) >> 23); + x7 ^= x6; + x0 += x7; + x1 -= x0 ^ ((~x7) << 19); + x2 ^= x1; + x3 += x2; + x4 -= x3 ^ ((~x2) >> 23); + x5 ^= x4; + x6 += x5; + x7 -= x6 ^ 0x0123456789ABCDEFULL; + + PASS(a, b, c, 9); + + swap = a; + a = c; + c = b; + b = swap; + } + + a ^= aa; + b -= bb; + c += cc; + + state[0] = a; + state[1] = b; + state[2] = c; +} + +#ifdef ARCH_BIGENDIAN +void tiger_fix_endian(uint8_t *s) { + uint64_t *i; + uint8_t *b, btemp; + uint16_t *w, wtemp; + + for(w = (uint16_t *)s; w < ((uint16_t *)s) + 12; w++) { + b = (uint8_t *)w; + btemp = *b; + *b = *(b + 1); + *(b + 1) = btemp; + } + + for(i = (uint64_t *)s; i < ((uint64_t *)s) + 3; i++) { + w = (uint16_t *)i; + wtemp = *w; + *w = *(w + 3); + *(w + 3) = wtemp; + wtemp = *(w + 1); + *(w + 1) = *(w + 2); + *(w + 2) = wtemp; + } +} +#endif + +void tiger(uint64_t* str, uint64_t length, uint64_t res[3]) { + uint64_t i, j; + uint8_t temp[64]; + res[0]= 0x0123456789ABCDEFULL; + res[1]= 0xFEDCBA9876543210ULL; + res[2]= 0xF096A5B4C3B2E187ULL; + + for (i = length; i >= 64; i -= 64) { +#ifdef ARCH_BIGENDIAN + for(j = 0; j < 64; j++) temp[j ^ 7] = ((uint8_t*) str)[j]; + tiger_compress(((uint64_t*) temp), res); +#else + tiger_compress(str, res); +#endif + str += 8; + } + +#ifdef ARCH_BIGENDIAN + for (j = 0; j < i; j++) temp[j ^ 7] = ((uint8_t*) str)[j]; + temp[j++ ^ 7] = 0x01; + for (; j & 7; j++) temp[j ^ 7] = 0; +#else + for (j = 0; j < i; j++) temp[j] = ((uint8_t*) str)[j]; + temp[j++] = 0x01; + for (; j & 7; j++) temp[j] = 0; +#endif + if (j > 56) { + for(; j < 64; j++) temp[j] = 0; + tiger_compress(((uint64_t*) temp), res); + j = 0; + } + + for (; j < 56; j++) temp[j] = 0; + + ((uint64_t*) (&(temp[56])))[0] = ((uint64_t) length) << 3; + tiger_compress(((uint64_t*) temp), res); + +#ifdef ARCH_BIGENDIAN + tiger_fix_endian((uint8_t*) res); +#endif +} + + +uint64_t tiger_sboxes[1024] = { + 0x02AAB17CF7E90C5EULL, + 0xAC424B03E243A8ECULL, + 0x72CD5BE30DD5FCD3ULL, + 0x6D019B93F6F97F3AULL, + 0xCD9978FFD21F9193ULL, + 0x7573A1C9708029E2ULL, + 0xB164326B922A83C3ULL, + 0x46883EEE04915870ULL, + 0xEAACE3057103ECE6ULL, + 0xC54169B808A3535CULL, + 0x4CE754918DDEC47CULL, + 0x0AA2F4DFDC0DF40CULL, + 0x10B76F18A74DBEFAULL, + 0xC6CCB6235AD1AB6AULL, + 0x13726121572FE2FFULL, + 0x1A488C6F199D921EULL, + 0x4BC9F9F4DA0007CAULL, + 0x26F5E6F6E85241C7ULL, + 0x859079DBEA5947B6ULL, + 0x4F1885C5C99E8C92ULL, + 0xD78E761EA96F864BULL, + 0x8E36428C52B5C17DULL, + 0x69CF6827373063C1ULL, + 0xB607C93D9BB4C56EULL, + 0x7D820E760E76B5EAULL, + 0x645C9CC6F07FDC42ULL, + 0xBF38A078243342E0ULL, + 0x5F6B343C9D2E7D04ULL, + 0xF2C28AEB600B0EC6ULL, + 0x6C0ED85F7254BCACULL, + 0x71592281A4DB4FE5ULL, + 0x1967FA69CE0FED9FULL, + 0xFD5293F8B96545DBULL, + 0xC879E9D7F2A7600BULL, + 0x860248920193194EULL, + 0xA4F9533B2D9CC0B3ULL, + 0x9053836C15957613ULL, + 0xDB6DCF8AFC357BF1ULL, + 0x18BEEA7A7A370F57ULL, + 0x037117CA50B99066ULL, + 0x6AB30A9774424A35ULL, + 0xF4E92F02E325249BULL, + 0x7739DB07061CCAE1ULL, + 0xD8F3B49CECA42A05ULL, + 0xBD56BE3F51382F73ULL, + 0x45FAED5843B0BB28ULL, + 0x1C813D5C11BF1F83ULL, + 0x8AF0E4B6D75FA169ULL, + 0x33EE18A487AD9999ULL, + 0x3C26E8EAB1C94410ULL, + 0xB510102BC0A822F9ULL, + 0x141EEF310CE6123BULL, + 0xFC65B90059DDB154ULL, + 0xE0158640C5E0E607ULL, + 0x884E079826C3A3CFULL, + 0x930D0D9523C535FDULL, + 0x35638D754E9A2B00ULL, + 0x4085FCCF40469DD5ULL, + 0xC4B17AD28BE23A4CULL, + 0xCAB2F0FC6A3E6A2EULL, + 0x2860971A6B943FCDULL, + 0x3DDE6EE212E30446ULL, + 0x6222F32AE01765AEULL, + 0x5D550BB5478308FEULL, + 0xA9EFA98DA0EDA22AULL, + 0xC351A71686C40DA7ULL, + 0x1105586D9C867C84ULL, + 0xDCFFEE85FDA22853ULL, + 0xCCFBD0262C5EEF76ULL, + 0xBAF294CB8990D201ULL, + 0xE69464F52AFAD975ULL, + 0x94B013AFDF133E14ULL, + 0x06A7D1A32823C958ULL, + 0x6F95FE5130F61119ULL, + 0xD92AB34E462C06C0ULL, + 0xED7BDE33887C71D2ULL, + 0x79746D6E6518393EULL, + 0x5BA419385D713329ULL, + 0x7C1BA6B948A97564ULL, + 0x31987C197BFDAC67ULL, + 0xDE6C23C44B053D02ULL, + 0x581C49FED002D64DULL, + 0xDD474D6338261571ULL, + 0xAA4546C3E473D062ULL, + 0x928FCE349455F860ULL, + 0x48161BBACAAB94D9ULL, + 0x63912430770E6F68ULL, + 0x6EC8A5E602C6641CULL, + 0x87282515337DDD2BULL, + 0x2CDA6B42034B701BULL, + 0xB03D37C181CB096DULL, + 0xE108438266C71C6FULL, + 0x2B3180C7EB51B255ULL, + 0xDF92B82F96C08BBCULL, + 0x5C68C8C0A632F3BAULL, + 0x5504CC861C3D0556ULL, + 0xABBFA4E55FB26B8FULL, + 0x41848B0AB3BACEB4ULL, + 0xB334A273AA445D32ULL, + 0xBCA696F0A85AD881ULL, + 0x24F6EC65B528D56CULL, + 0x0CE1512E90F4524AULL, + 0x4E9DD79D5506D35AULL, + 0x258905FAC6CE9779ULL, + 0x2019295B3E109B33ULL, + 0xF8A9478B73A054CCULL, + 0x2924F2F934417EB0ULL, + 0x3993357D536D1BC4ULL, + 0x38A81AC21DB6FF8BULL, + 0x47C4FBF17D6016BFULL, + 0x1E0FAADD7667E3F5ULL, + 0x7ABCFF62938BEB96ULL, + 0xA78DAD948FC179C9ULL, + 0x8F1F98B72911E50DULL, + 0x61E48EAE27121A91ULL, + 0x4D62F7AD31859808ULL, + 0xECEBA345EF5CEAEBULL, + 0xF5CEB25EBC9684CEULL, + 0xF633E20CB7F76221ULL, + 0xA32CDF06AB8293E4ULL, + 0x985A202CA5EE2CA4ULL, + 0xCF0B8447CC8A8FB1ULL, + 0x9F765244979859A3ULL, + 0xA8D516B1A1240017ULL, + 0x0BD7BA3EBB5DC726ULL, + 0xE54BCA55B86ADB39ULL, + 0x1D7A3AFD6C478063ULL, + 0x519EC608E7669EDDULL, + 0x0E5715A2D149AA23ULL, + 0x177D4571848FF194ULL, + 0xEEB55F3241014C22ULL, + 0x0F5E5CA13A6E2EC2ULL, + 0x8029927B75F5C361ULL, + 0xAD139FABC3D6E436ULL, + 0x0D5DF1A94CCF402FULL, + 0x3E8BD948BEA5DFC8ULL, + 0xA5A0D357BD3FF77EULL, + 0xA2D12E251F74F645ULL, + 0x66FD9E525E81A082ULL, + 0x2E0C90CE7F687A49ULL, + 0xC2E8BCBEBA973BC5ULL, + 0x000001BCE509745FULL, + 0x423777BBE6DAB3D6ULL, + 0xD1661C7EAEF06EB5ULL, + 0xA1781F354DAACFD8ULL, + 0x2D11284A2B16AFFCULL, + 0xF1FC4F67FA891D1FULL, + 0x73ECC25DCB920ADAULL, + 0xAE610C22C2A12651ULL, + 0x96E0A810D356B78AULL, + 0x5A9A381F2FE7870FULL, + 0xD5AD62EDE94E5530ULL, + 0xD225E5E8368D1427ULL, + 0x65977B70C7AF4631ULL, + 0x99F889B2DE39D74FULL, + 0x233F30BF54E1D143ULL, + 0x9A9675D3D9A63C97ULL, + 0x5470554FF334F9A8ULL, + 0x166ACB744A4F5688ULL, + 0x70C74CAAB2E4AEADULL, + 0xF0D091646F294D12ULL, + 0x57B82A89684031D1ULL, + 0xEFD95A5A61BE0B6BULL, + 0x2FBD12E969F2F29AULL, + 0x9BD37013FEFF9FE8ULL, + 0x3F9B0404D6085A06ULL, + 0x4940C1F3166CFE15ULL, + 0x09542C4DCDF3DEFBULL, + 0xB4C5218385CD5CE3ULL, + 0xC935B7DC4462A641ULL, + 0x3417F8A68ED3B63FULL, + 0xB80959295B215B40ULL, + 0xF99CDAEF3B8C8572ULL, + 0x018C0614F8FCB95DULL, + 0x1B14ACCD1A3ACDF3ULL, + 0x84D471F200BB732DULL, + 0xC1A3110E95E8DA16ULL, + 0x430A7220BF1A82B8ULL, + 0xB77E090D39DF210EULL, + 0x5EF4BD9F3CD05E9DULL, + 0x9D4FF6DA7E57A444ULL, + 0xDA1D60E183D4A5F8ULL, + 0xB287C38417998E47ULL, + 0xFE3EDC121BB31886ULL, + 0xC7FE3CCC980CCBEFULL, + 0xE46FB590189BFD03ULL, + 0x3732FD469A4C57DCULL, + 0x7EF700A07CF1AD65ULL, + 0x59C64468A31D8859ULL, + 0x762FB0B4D45B61F6ULL, + 0x155BAED099047718ULL, + 0x68755E4C3D50BAA6ULL, + 0xE9214E7F22D8B4DFULL, + 0x2ADDBF532EAC95F4ULL, + 0x32AE3909B4BD0109ULL, + 0x834DF537B08E3450ULL, + 0xFA209DA84220728DULL, + 0x9E691D9B9EFE23F7ULL, + 0x0446D288C4AE8D7FULL, + 0x7B4CC524E169785BULL, + 0x21D87F0135CA1385ULL, + 0xCEBB400F137B8AA5ULL, + 0x272E2B66580796BEULL, + 0x3612264125C2B0DEULL, + 0x057702BDAD1EFBB2ULL, + 0xD4BABB8EACF84BE9ULL, + 0x91583139641BC67BULL, + 0x8BDC2DE08036E024ULL, + 0x603C8156F49F68EDULL, + 0xF7D236F7DBEF5111ULL, + 0x9727C4598AD21E80ULL, + 0xA08A0896670A5FD7ULL, + 0xCB4A8F4309EBA9CBULL, + 0x81AF564B0F7036A1ULL, + 0xC0B99AA778199ABDULL, + 0x959F1EC83FC8E952ULL, + 0x8C505077794A81B9ULL, + 0x3ACAAF8F056338F0ULL, + 0x07B43F50627A6778ULL, + 0x4A44AB49F5ECCC77ULL, + 0x3BC3D6E4B679EE98ULL, + 0x9CC0D4D1CF14108CULL, + 0x4406C00B206BC8A0ULL, + 0x82A18854C8D72D89ULL, + 0x67E366B35C3C432CULL, + 0xB923DD61102B37F2ULL, + 0x56AB2779D884271DULL, + 0xBE83E1B0FF1525AFULL, + 0xFB7C65D4217E49A9ULL, + 0x6BDBE0E76D48E7D4ULL, + 0x08DF828745D9179EULL, + 0x22EA6A9ADD53BD34ULL, + 0xE36E141C5622200AULL, + 0x7F805D1B8CB750EEULL, + 0xAFE5C7A59F58E837ULL, + 0xE27F996A4FB1C23CULL, + 0xD3867DFB0775F0D0ULL, + 0xD0E673DE6E88891AULL, + 0x123AEB9EAFB86C25ULL, + 0x30F1D5D5C145B895ULL, + 0xBB434A2DEE7269E7ULL, + 0x78CB67ECF931FA38ULL, + 0xF33B0372323BBF9CULL, + 0x52D66336FB279C74ULL, + 0x505F33AC0AFB4EAAULL, + 0xE8A5CD99A2CCE187ULL, + 0x534974801E2D30BBULL, + 0x8D2D5711D5876D90ULL, + 0x1F1A412891BC038EULL, + 0xD6E2E71D82E56648ULL, + 0x74036C3A497732B7ULL, + 0x89B67ED96361F5ABULL, + 0xFFED95D8F1EA02A2ULL, + 0xE72B3BD61464D43DULL, + 0xA6300F170BDC4820ULL, + 0xEBC18760ED78A77AULL, + 0xE6A6BE5A05A12138ULL, + 0xB5A122A5B4F87C98ULL, + 0x563C6089140B6990ULL, + 0x4C46CB2E391F5DD5ULL, + 0xD932ADDBC9B79434ULL, + 0x08EA70E42015AFF5ULL, + 0xD765A6673E478CF1ULL, + 0xC4FB757EAB278D99ULL, + 0xDF11C6862D6E0692ULL, + 0xDDEB84F10D7F3B16ULL, + 0x6F2EF604A665EA04ULL, + 0x4A8E0F0FF0E0DFB3ULL, + 0xA5EDEEF83DBCBA51ULL, + 0xFC4F0A2A0EA4371EULL, + 0xE83E1DA85CB38429ULL, + 0xDC8FF882BA1B1CE2ULL, + 0xCD45505E8353E80DULL, + 0x18D19A00D4DB0717ULL, + 0x34A0CFEDA5F38101ULL, + 0x0BE77E518887CAF2ULL, + 0x1E341438B3C45136ULL, + 0xE05797F49089CCF9ULL, + 0xFFD23F9DF2591D14ULL, + 0x543DDA228595C5CDULL, + 0x661F81FD99052A33ULL, + 0x8736E641DB0F7B76ULL, + 0x15227725418E5307ULL, + 0xE25F7F46162EB2FAULL, + 0x48A8B2126C13D9FEULL, + 0xAFDC541792E76EEAULL, + 0x03D912BFC6D1898FULL, + 0x31B1AAFA1B83F51BULL, + 0xF1AC2796E42AB7D9ULL, + 0x40A3A7D7FCD2EBACULL, + 0x1056136D0AFBBCC5ULL, + 0x7889E1DD9A6D0C85ULL, + 0xD33525782A7974AAULL, + 0xA7E25D09078AC09BULL, + 0xBD4138B3EAC6EDD0ULL, + 0x920ABFBE71EB9E70ULL, + 0xA2A5D0F54FC2625CULL, + 0xC054E36B0B1290A3ULL, + 0xF6DD59FF62FE932BULL, + 0x3537354511A8AC7DULL, + 0xCA845E9172FADCD4ULL, + 0x84F82B60329D20DCULL, + 0x79C62CE1CD672F18ULL, + 0x8B09A2ADD124642CULL, + 0xD0C1E96A19D9E726ULL, + 0x5A786A9B4BA9500CULL, + 0x0E020336634C43F3ULL, + 0xC17B474AEB66D822ULL, + 0x6A731AE3EC9BAAC2ULL, + 0x8226667AE0840258ULL, + 0x67D4567691CAECA5ULL, + 0x1D94155C4875ADB5ULL, + 0x6D00FD985B813FDFULL, + 0x51286EFCB774CD06ULL, + 0x5E8834471FA744AFULL, + 0xF72CA0AEE761AE2EULL, + 0xBE40E4CDAEE8E09AULL, + 0xE9970BBB5118F665ULL, + 0x726E4BEB33DF1964ULL, + 0x703B000729199762ULL, + 0x4631D816F5EF30A7ULL, + 0xB880B5B51504A6BEULL, + 0x641793C37ED84B6CULL, + 0x7B21ED77F6E97D96ULL, + 0x776306312EF96B73ULL, + 0xAE528948E86FF3F4ULL, + 0x53DBD7F286A3F8F8ULL, + 0x16CADCE74CFC1063ULL, + 0x005C19BDFA52C6DDULL, + 0x68868F5D64D46AD3ULL, + 0x3A9D512CCF1E186AULL, + 0x367E62C2385660AEULL, + 0xE359E7EA77DCB1D7ULL, + 0x526C0773749ABE6EULL, + 0x735AE5F9D09F734BULL, + 0x493FC7CC8A558BA8ULL, + 0xB0B9C1533041AB45ULL, + 0x321958BA470A59BDULL, + 0x852DB00B5F46C393ULL, + 0x91209B2BD336B0E5ULL, + 0x6E604F7D659EF19FULL, + 0xB99A8AE2782CCB24ULL, + 0xCCF52AB6C814C4C7ULL, + 0x4727D9AFBE11727BULL, + 0x7E950D0C0121B34DULL, + 0x756F435670AD471FULL, + 0xF5ADD442615A6849ULL, + 0x4E87E09980B9957AULL, + 0x2ACFA1DF50AEE355ULL, + 0xD898263AFD2FD556ULL, + 0xC8F4924DD80C8FD6ULL, + 0xCF99CA3D754A173AULL, + 0xFE477BACAF91BF3CULL, + 0xED5371F6D690C12DULL, + 0x831A5C285E687094ULL, + 0xC5D3C90A3708A0A4ULL, + 0x0F7F903717D06580ULL, + 0x19F9BB13B8FDF27FULL, + 0xB1BD6F1B4D502843ULL, + 0x1C761BA38FFF4012ULL, + 0x0D1530C4E2E21F3BULL, + 0x8943CE69A7372C8AULL, + 0xE5184E11FEB5CE66ULL, + 0x618BDB80BD736621ULL, + 0x7D29BAD68B574D0BULL, + 0x81BB613E25E6FE5BULL, + 0x071C9C10BC07913FULL, + 0xC7BEEB7909AC2D97ULL, + 0xC3E58D353BC5D757ULL, + 0xEB017892F38F61E8ULL, + 0xD4EFFB9C9B1CC21AULL, + 0x99727D26F494F7ABULL, + 0xA3E063A2956B3E03ULL, + 0x9D4A8B9A4AA09C30ULL, + 0x3F6AB7D500090FB4ULL, + 0x9CC0F2A057268AC0ULL, + 0x3DEE9D2DEDBF42D1ULL, + 0x330F49C87960A972ULL, + 0xC6B2720287421B41ULL, + 0x0AC59EC07C00369CULL, + 0xEF4EAC49CB353425ULL, + 0xF450244EEF0129D8ULL, + 0x8ACC46E5CAF4DEB6ULL, + 0x2FFEAB63989263F7ULL, + 0x8F7CB9FE5D7A4578ULL, + 0x5BD8F7644E634635ULL, + 0x427A7315BF2DC900ULL, + 0x17D0C4AA2125261CULL, + 0x3992486C93518E50ULL, + 0xB4CBFEE0A2D7D4C3ULL, + 0x7C75D6202C5DDD8DULL, + 0xDBC295D8E35B6C61ULL, + 0x60B369D302032B19ULL, + 0xCE42685FDCE44132ULL, + 0x06F3DDB9DDF65610ULL, + 0x8EA4D21DB5E148F0ULL, + 0x20B0FCE62FCD496FULL, + 0x2C1B912358B0EE31ULL, + 0xB28317B818F5A308ULL, + 0xA89C1E189CA6D2CFULL, + 0x0C6B18576AAADBC8ULL, + 0xB65DEAA91299FAE3ULL, + 0xFB2B794B7F1027E7ULL, + 0x04E4317F443B5BEBULL, + 0x4B852D325939D0A6ULL, + 0xD5AE6BEEFB207FFCULL, + 0x309682B281C7D374ULL, + 0xBAE309A194C3B475ULL, + 0x8CC3F97B13B49F05ULL, + 0x98A9422FF8293967ULL, + 0x244B16B01076FF7CULL, + 0xF8BF571C663D67EEULL, + 0x1F0D6758EEE30DA1ULL, + 0xC9B611D97ADEB9B7ULL, + 0xB7AFD5887B6C57A2ULL, + 0x6290AE846B984FE1ULL, + 0x94DF4CDEACC1A5FDULL, + 0x058A5BD1C5483AFFULL, + 0x63166CC142BA3C37ULL, + 0x8DB8526EB2F76F40ULL, + 0xE10880036F0D6D4EULL, + 0x9E0523C9971D311DULL, + 0x45EC2824CC7CD691ULL, + 0x575B8359E62382C9ULL, + 0xFA9E400DC4889995ULL, + 0xD1823ECB45721568ULL, + 0xDAFD983B8206082FULL, + 0xAA7D29082386A8CBULL, + 0x269FCD4403B87588ULL, + 0x1B91F5F728BDD1E0ULL, + 0xE4669F39040201F6ULL, + 0x7A1D7C218CF04ADEULL, + 0x65623C29D79CE5CEULL, + 0x2368449096C00BB1ULL, + 0xAB9BF1879DA503BAULL, + 0xBC23ECB1A458058EULL, + 0x9A58DF01BB401ECCULL, + 0xA070E868A85F143DULL, + 0x4FF188307DF2239EULL, + 0x14D565B41A641183ULL, + 0xEE13337452701602ULL, + 0x950E3DCF3F285E09ULL, + 0x59930254B9C80953ULL, + 0x3BF299408930DA6DULL, + 0xA955943F53691387ULL, + 0xA15EDECAA9CB8784ULL, + 0x29142127352BE9A0ULL, + 0x76F0371FFF4E7AFBULL, + 0x0239F450274F2228ULL, + 0xBB073AF01D5E868BULL, + 0xBFC80571C10E96C1ULL, + 0xD267088568222E23ULL, + 0x9671A3D48E80B5B0ULL, + 0x55B5D38AE193BB81ULL, + 0x693AE2D0A18B04B8ULL, + 0x5C48B4ECADD5335FULL, + 0xFD743B194916A1CAULL, + 0x2577018134BE98C4ULL, + 0xE77987E83C54A4ADULL, + 0x28E11014DA33E1B9ULL, + 0x270CC59E226AA213ULL, + 0x71495F756D1A5F60ULL, + 0x9BE853FB60AFEF77ULL, + 0xADC786A7F7443DBFULL, + 0x0904456173B29A82ULL, + 0x58BC7A66C232BD5EULL, + 0xF306558C673AC8B2ULL, + 0x41F639C6B6C9772AULL, + 0x216DEFE99FDA35DAULL, + 0x11640CC71C7BE615ULL, + 0x93C43694565C5527ULL, + 0xEA038E6246777839ULL, + 0xF9ABF3CE5A3E2469ULL, + 0x741E768D0FD312D2ULL, + 0x0144B883CED652C6ULL, + 0xC20B5A5BA33F8552ULL, + 0x1AE69633C3435A9DULL, + 0x97A28CA4088CFDECULL, + 0x8824A43C1E96F420ULL, + 0x37612FA66EEEA746ULL, + 0x6B4CB165F9CF0E5AULL, + 0x43AA1C06A0ABFB4AULL, + 0x7F4DC26FF162796BULL, + 0x6CBACC8E54ED9B0FULL, + 0xA6B7FFEFD2BB253EULL, + 0x2E25BC95B0A29D4FULL, + 0x86D6A58BDEF1388CULL, + 0xDED74AC576B6F054ULL, + 0x8030BDBC2B45805DULL, + 0x3C81AF70E94D9289ULL, + 0x3EFF6DDA9E3100DBULL, + 0xB38DC39FDFCC8847ULL, + 0x123885528D17B87EULL, + 0xF2DA0ED240B1B642ULL, + 0x44CEFADCD54BF9A9ULL, + 0x1312200E433C7EE6ULL, + 0x9FFCC84F3A78C748ULL, + 0xF0CD1F72248576BBULL, + 0xEC6974053638CFE4ULL, + 0x2BA7B67C0CEC4E4CULL, + 0xAC2F4DF3E5CE32EDULL, + 0xCB33D14326EA4C11ULL, + 0xA4E9044CC77E58BCULL, + 0x5F513293D934FCEFULL, + 0x5DC9645506E55444ULL, + 0x50DE418F317DE40AULL, + 0x388CB31A69DDE259ULL, + 0x2DB4A83455820A86ULL, + 0x9010A91E84711AE9ULL, + 0x4DF7F0B7B1498371ULL, + 0xD62A2EABC0977179ULL, + 0x22FAC097AA8D5C0EULL, + 0xF49FCC2FF1DAF39BULL, + 0x487FD5C66FF29281ULL, + 0xE8A30667FCDCA83FULL, + 0x2C9B4BE3D2FCCE63ULL, + 0xDA3FF74B93FBBBC2ULL, + 0x2FA165D2FE70BA66ULL, + 0xA103E279970E93D4ULL, + 0xBECDEC77B0E45E71ULL, + 0xCFB41E723985E497ULL, + 0xB70AAA025EF75017ULL, + 0xD42309F03840B8E0ULL, + 0x8EFC1AD035898579ULL, + 0x96C6920BE2B2ABC5ULL, + 0x66AF4163375A9172ULL, + 0x2174ABDCCA7127FBULL, + 0xB33CCEA64A72FF41ULL, + 0xF04A4933083066A5ULL, + 0x8D970ACDD7289AF5ULL, + 0x8F96E8E031C8C25EULL, + 0xF3FEC02276875D47ULL, + 0xEC7BF310056190DDULL, + 0xF5ADB0AEBB0F1491ULL, + 0x9B50F8850FD58892ULL, + 0x4975488358B74DE8ULL, + 0xA3354FF691531C61ULL, + 0x0702BBE481D2C6EEULL, + 0x89FB24057DEDED98ULL, + 0xAC3075138596E902ULL, + 0x1D2D3580172772EDULL, + 0xEB738FC28E6BC30DULL, + 0x5854EF8F63044326ULL, + 0x9E5C52325ADD3BBEULL, + 0x90AA53CF325C4623ULL, + 0xC1D24D51349DD067ULL, + 0x2051CFEEA69EA624ULL, + 0x13220F0A862E7E4FULL, + 0xCE39399404E04864ULL, + 0xD9C42CA47086FCB7ULL, + 0x685AD2238A03E7CCULL, + 0x066484B2AB2FF1DBULL, + 0xFE9D5D70EFBF79ECULL, + 0x5B13B9DD9C481854ULL, + 0x15F0D475ED1509ADULL, + 0x0BEBCD060EC79851ULL, + 0xD58C6791183AB7F8ULL, + 0xD1187C5052F3EEE4ULL, + 0xC95D1192E54E82FFULL, + 0x86EEA14CB9AC6CA2ULL, + 0x3485BEB153677D5DULL, + 0xDD191D781F8C492AULL, + 0xF60866BAA784EBF9ULL, + 0x518F643BA2D08C74ULL, + 0x8852E956E1087C22ULL, + 0xA768CB8DC410AE8DULL, + 0x38047726BFEC8E1AULL, + 0xA67738B4CD3B45AAULL, + 0xAD16691CEC0DDE19ULL, + 0xC6D4319380462E07ULL, + 0xC5A5876D0BA61938ULL, + 0x16B9FA1FA58FD840ULL, + 0x188AB1173CA74F18ULL, + 0xABDA2F98C99C021FULL, + 0x3E0580AB134AE816ULL, + 0x5F3B05B773645ABBULL, + 0x2501A2BE5575F2F6ULL, + 0x1B2F74004E7E8BA9ULL, + 0x1CD7580371E8D953ULL, + 0x7F6ED89562764E30ULL, + 0xB15926FF596F003DULL, + 0x9F65293DA8C5D6B9ULL, + 0x6ECEF04DD690F84CULL, + 0x4782275FFF33AF88ULL, + 0xE41433083F820801ULL, + 0xFD0DFE409A1AF9B5ULL, + 0x4325A3342CDB396BULL, + 0x8AE77E62B301B252ULL, + 0xC36F9E9F6655615AULL, + 0x85455A2D92D32C09ULL, + 0xF2C7DEA949477485ULL, + 0x63CFB4C133A39EBAULL, + 0x83B040CC6EBC5462ULL, + 0x3B9454C8FDB326B0ULL, + 0x56F56A9E87FFD78CULL, + 0x2DC2940D99F42BC6ULL, + 0x98F7DF096B096E2DULL, + 0x19A6E01E3AD852BFULL, + 0x42A99CCBDBD4B40BULL, + 0xA59998AF45E9C559ULL, + 0x366295E807D93186ULL, + 0x6B48181BFAA1F773ULL, + 0x1FEC57E2157A0A1DULL, + 0x4667446AF6201AD5ULL, + 0xE615EBCACFB0F075ULL, + 0xB8F31F4F68290778ULL, + 0x22713ED6CE22D11EULL, + 0x3057C1A72EC3C93BULL, + 0xCB46ACC37C3F1F2FULL, + 0xDBB893FD02AAF50EULL, + 0x331FD92E600B9FCFULL, + 0xA498F96148EA3AD6ULL, + 0xA8D8426E8B6A83EAULL, + 0xA089B274B7735CDCULL, + 0x87F6B3731E524A11ULL, + 0x118808E5CBC96749ULL, + 0x9906E4C7B19BD394ULL, + 0xAFED7F7E9B24A20CULL, + 0x6509EADEEB3644A7ULL, + 0x6C1EF1D3E8EF0EDEULL, + 0xB9C97D43E9798FB4ULL, + 0xA2F2D784740C28A3ULL, + 0x7B8496476197566FULL, + 0x7A5BE3E6B65F069DULL, + 0xF96330ED78BE6F10ULL, + 0xEEE60DE77A076A15ULL, + 0x2B4BEE4AA08B9BD0ULL, + 0x6A56A63EC7B8894EULL, + 0x02121359BA34FEF4ULL, + 0x4CBF99F8283703FCULL, + 0x398071350CAF30C8ULL, + 0xD0A77A89F017687AULL, + 0xF1C1A9EB9E423569ULL, + 0x8C7976282DEE8199ULL, + 0x5D1737A5DD1F7ABDULL, + 0x4F53433C09A9FA80ULL, + 0xFA8B0C53DF7CA1D9ULL, + 0x3FD9DCBC886CCB77ULL, + 0xC040917CA91B4720ULL, + 0x7DD00142F9D1DCDFULL, + 0x8476FC1D4F387B58ULL, + 0x23F8E7C5F3316503ULL, + 0x032A2244E7E37339ULL, + 0x5C87A5D750F5A74BULL, + 0x082B4CC43698992EULL, + 0xDF917BECB858F63CULL, + 0x3270B8FC5BF86DDAULL, + 0x10AE72BB29B5DD76ULL, + 0x576AC94E7700362BULL, + 0x1AD112DAC61EFB8FULL, + 0x691BC30EC5FAA427ULL, + 0xFF246311CC327143ULL, + 0x3142368E30E53206ULL, + 0x71380E31E02CA396ULL, + 0x958D5C960AAD76F1ULL, + 0xF8D6F430C16DA536ULL, + 0xC8FFD13F1BE7E1D2ULL, + 0x7578AE66004DDBE1ULL, + 0x05833F01067BE646ULL, + 0xBB34B5AD3BFE586DULL, + 0x095F34C9A12B97F0ULL, + 0x247AB64525D60CA8ULL, + 0xDCDBC6F3017477D1ULL, + 0x4A2E14D4DECAD24DULL, + 0xBDB5E6D9BE0A1EEBULL, + 0x2A7E70F7794301ABULL, + 0xDEF42D8A270540FDULL, + 0x01078EC0A34C22C1ULL, + 0xE5DE511AF4C16387ULL, + 0x7EBB3A52BD9A330AULL, + 0x77697857AA7D6435ULL, + 0x004E831603AE4C32ULL, + 0xE7A21020AD78E312ULL, + 0x9D41A70C6AB420F2ULL, + 0x28E06C18EA1141E6ULL, + 0xD2B28CBD984F6B28ULL, + 0x26B75F6C446E9D83ULL, + 0xBA47568C4D418D7FULL, + 0xD80BADBFE6183D8EULL, + 0x0E206D7F5F166044ULL, + 0xE258A43911CBCA3EULL, + 0x723A1746B21DC0BCULL, + 0xC7CAA854F5D7CDD3ULL, + 0x7CAC32883D261D9CULL, + 0x7690C26423BA942CULL, + 0x17E55524478042B8ULL, + 0xE0BE477656A2389FULL, + 0x4D289B5E67AB2DA0ULL, + 0x44862B9C8FBBFD31ULL, + 0xB47CC8049D141365ULL, + 0x822C1B362B91C793ULL, + 0x4EB14655FB13DFD8ULL, + 0x1ECBBA0714E2A97BULL, + 0x6143459D5CDE5F14ULL, + 0x53A8FBF1D5F0AC89ULL, + 0x97EA04D81C5E5B00ULL, + 0x622181A8D4FDB3F3ULL, + 0xE9BCD341572A1208ULL, + 0x1411258643CCE58AULL, + 0x9144C5FEA4C6E0A4ULL, + 0x0D33D06565CF620FULL, + 0x54A48D489F219CA1ULL, + 0xC43E5EAC6D63C821ULL, + 0xA9728B3A72770DAFULL, + 0xD7934E7B20DF87EFULL, + 0xE35503B61A3E86E5ULL, + 0xCAE321FBC819D504ULL, + 0x129A50B3AC60BFA6ULL, + 0xCD5E68EA7E9FB6C3ULL, + 0xB01C90199483B1C7ULL, + 0x3DE93CD5C295376CULL, + 0xAED52EDF2AB9AD13ULL, + 0x2E60F512C0A07884ULL, + 0xBC3D86A3E36210C9ULL, + 0x35269D9B163951CEULL, + 0x0C7D6E2AD0CDB5FAULL, + 0x59E86297D87F5733ULL, + 0x298EF221898DB0E7ULL, + 0x55000029D1A5AA7EULL, + 0x8BC08AE1B5061B45ULL, + 0xC2C31C2B6C92703AULL, + 0x94CC596BAF25EF42ULL, + 0x0A1D73DB22540456ULL, + 0x04B6A0F9D9C4179AULL, + 0xEFFDAFA2AE3D3C60ULL, + 0xF7C8075BB49496C4ULL, + 0x9CC5C7141D1CD4E3ULL, + 0x78BD1638218E5534ULL, + 0xB2F11568F850246AULL, + 0xEDFABCFA9502BC29ULL, + 0x796CE5F2DA23051BULL, + 0xAAE128B0DC93537CULL, + 0x3A493DA0EE4B29AEULL, + 0xB5DF6B2C416895D7ULL, + 0xFCABBD25122D7F37ULL, + 0x70810B58105DC4B1ULL, + 0xE10FDD37F7882A90ULL, + 0x524DCAB5518A3F5CULL, + 0x3C9E85878451255BULL, + 0x4029828119BD34E2ULL, + 0x74A05B6F5D3CECCBULL, + 0xB610021542E13ECAULL, + 0x0FF979D12F59E2ACULL, + 0x6037DA27E4F9CC50ULL, + 0x5E92975A0DF1847DULL, + 0xD66DE190D3E623FEULL, + 0x5032D6B87B568048ULL, + 0x9A36B7CE8235216EULL, + 0x80272A7A24F64B4AULL, + 0x93EFED8B8C6916F7ULL, + 0x37DDBFF44CCE1555ULL, + 0x4B95DB5D4B99BD25ULL, + 0x92D3FDA169812FC0ULL, + 0xFB1A4A9A90660BB6ULL, + 0x730C196946A4B9B2ULL, + 0x81E289AA7F49DA68ULL, + 0x64669A0F83B1A05FULL, + 0x27B3FF7D9644F48BULL, + 0xCC6B615C8DB675B3ULL, + 0x674F20B9BCEBBE95ULL, + 0x6F31238275655982ULL, + 0x5AE488713E45CF05ULL, + 0xBF619F9954C21157ULL, + 0xEABAC46040A8EAE9ULL, + 0x454C6FE9F2C0C1CDULL, + 0x419CF6496412691CULL, + 0xD3DC3BEF265B0F70ULL, + 0x6D0E60F5C3578A9EULL, + 0x5B0E608526323C55ULL, + 0x1A46C1A9FA1B59F5ULL, + 0xA9E245A17C4C8FFAULL, + 0x65CA5159DB2955D7ULL, + 0x05DB0A76CE35AFC2ULL, + 0x81EAC77EA9113D45ULL, + 0x528EF88AB6AC0A0DULL, + 0xA09EA253597BE3FFULL, + 0x430DDFB3AC48CD56ULL, + 0xC4B3A67AF45CE46FULL, + 0x4ECECFD8FBE2D05EULL, + 0x3EF56F10B39935F0ULL, + 0x0B22D6829CD619C6ULL, + 0x17FD460A74DF2069ULL, + 0x6CF8CC8E8510ED40ULL, + 0xD6C824BF3A6ECAA7ULL, + 0x61243D581A817049ULL, + 0x048BACB6BBC163A2ULL, + 0xD9A38AC27D44CC32ULL, + 0x7FDDFF5BAAF410ABULL, + 0xAD6D495AA804824BULL, + 0xE1A6A74F2D8C9F94ULL, + 0xD4F7851235DEE8E3ULL, + 0xFD4B7F886540D893ULL, + 0x247C20042AA4BFDAULL, + 0x096EA1C517D1327CULL, + 0xD56966B4361A6685ULL, + 0x277DA5C31221057DULL, + 0x94D59893A43ACFF7ULL, + 0x64F0C51CCDC02281ULL, + 0x3D33BCC4FF6189DBULL, + 0xE005CB184CE66AF1ULL, + 0xFF5CCD1D1DB99BEAULL, + 0xB0B854A7FE42980FULL, + 0x7BD46A6A718D4B9FULL, + 0xD10FA8CC22A5FD8CULL, + 0xD31484952BE4BD31ULL, + 0xC7FA975FCB243847ULL, + 0x4886ED1E5846C407ULL, + 0x28CDDB791EB70B04ULL, + 0xC2B00BE2F573417FULL, + 0x5C9590452180F877ULL, + 0x7A6BDDFFF370EB00ULL, + 0xCE509E38D6D9D6A4ULL, + 0xEBEB0F00647FA702ULL, + 0x1DCC06CF76606F06ULL, + 0xE4D9F28BA286FF0AULL, + 0xD85A305DC918C262ULL, + 0x475B1D8732225F54ULL, + 0x2D4FB51668CCB5FEULL, + 0xA679B9D9D72BBA20ULL, + 0x53841C0D912D43A5ULL, + 0x3B7EAA48BF12A4E8ULL, + 0x781E0E47F22F1DDFULL, + 0xEFF20CE60AB50973ULL, + 0x20D261D19DFFB742ULL, + 0x16A12B03062A2E39ULL, + 0x1960EB2239650495ULL, + 0x251C16FED50EB8B8ULL, + 0x9AC0C330F826016EULL, + 0xED152665953E7671ULL, + 0x02D63194A6369570ULL, + 0x5074F08394B1C987ULL, + 0x70BA598C90B25CE1ULL, + 0x794A15810B9742F6ULL, + 0x0D5925E9FCAF8C6CULL, + 0x3067716CD868744EULL, + 0x910AB077E8D7731BULL, + 0x6A61BBDB5AC42F61ULL, + 0x93513EFBF0851567ULL, + 0xF494724B9E83E9D5ULL, + 0xE887E1985C09648DULL, + 0x34B1D3C675370CFDULL, + 0xDC35E433BC0D255DULL, + 0xD0AAB84234131BE0ULL, + 0x08042A50B48B7EAFULL, + 0x9997C4EE44A3AB35ULL, + 0x829A7B49201799D0ULL, + 0x263B8307B7C54441ULL, + 0x752F95F4FD6A6CA6ULL, + 0x927217402C08C6E5ULL, + 0x2A8AB754A795D9EEULL, + 0xA442F7552F72943DULL, + 0x2C31334E19781208ULL, + 0x4FA98D7CEAEE6291ULL, + 0x55C3862F665DB309ULL, + 0xBD0610175D53B1F3ULL, + 0x46FE6CB840413F27ULL, + 0x3FE03792DF0CFA59ULL, + 0xCFE700372EB85E8FULL, + 0xA7BE29E7ADBCE118ULL, + 0xE544EE5CDE8431DDULL, + 0x8A781B1B41F1873EULL, + 0xA5C94C78A0D2F0E7ULL, + 0x39412E2877B60728ULL, + 0xA1265EF3AFC9A62CULL, + 0xBCC2770C6A2506C5ULL, + 0x3AB66DD5DCE1CE12ULL, + 0xE65499D04A675B37ULL, + 0x7D8F523481BFD216ULL, + 0x0F6F64FCEC15F389ULL, + 0x74EFBE618B5B13C8ULL, + 0xACDC82B714273E1DULL, + 0xDD40BFE003199D17ULL, + 0x37E99257E7E061F8ULL, + 0xFA52626904775AAAULL, + 0x8BBBF63A463D56F9ULL, + 0xF0013F1543A26E64ULL, + 0xA8307E9F879EC898ULL, + 0xCC4C27A4150177CCULL, + 0x1B432F2CCA1D3348ULL, + 0xDE1D1F8F9F6FA013ULL, + 0x606602A047A7DDD6ULL, + 0xD237AB64CC1CB2C7ULL, + 0x9B938E7225FCD1D3ULL, + 0xEC4E03708E0FF476ULL, + 0xFEB2FBDA3D03C12DULL, + 0xAE0BCED2EE43889AULL, + 0x22CB8923EBFB4F43ULL, + 0x69360D013CF7396DULL, + 0x855E3602D2D4E022ULL, + 0x073805BAD01F784CULL, + 0x33E17A133852F546ULL, + 0xDF4874058AC7B638ULL, + 0xBA92B29C678AA14AULL, + 0x0CE89FC76CFAADCDULL, + 0x5F9D4E0908339E34ULL, + 0xF1AFE9291F5923B9ULL, + 0x6E3480F60F4A265FULL, + 0xEEBF3A2AB29B841CULL, + 0xE21938A88F91B4ADULL, + 0x57DFEFF845C6D3C3ULL, + 0x2F006B0BF62CAAF2ULL, + 0x62F479EF6F75EE78ULL, + 0x11A55AD41C8916A9ULL, + 0xF229D29084FED453ULL, + 0x42F1C27B16B000E6ULL, + 0x2B1F76749823C074ULL, + 0x4B76ECA3C2745360ULL, + 0x8C98F463B91691BDULL, + 0x14BCC93CF1ADE66AULL, + 0x8885213E6D458397ULL, + 0x8E177DF0274D4711ULL, + 0xB49B73B5503F2951ULL, + 0x10168168C3F96B6BULL, + 0x0E3D963B63CAB0AEULL, + 0x8DFC4B5655A1DB14ULL, + 0xF789F1356E14DE5CULL, + 0x683E68AF4E51DAC1ULL, + 0xC9A84F9D8D4B0FD9ULL, + 0x3691E03F52A0F9D1ULL, + 0x5ED86E46E1878E80ULL, + 0x3C711A0E99D07150ULL, + 0x5A0865B20C4E9310ULL, + 0x56FBFC1FE4F0682EULL, + 0xEA8D5DE3105EDF9BULL, + 0x71ABFDB12379187AULL, + 0x2EB99DE1BEE77B9CULL, + 0x21ECC0EA33CF4523ULL, + 0x59A4D7521805C7A1ULL, + 0x3896F5EB56AE7C72ULL, + 0xAA638F3DB18F75DCULL, + 0x9F39358DABE9808EULL, + 0xB7DEFA91C00B72ACULL, + 0x6B5541FD62492D92ULL, + 0x6DC6DEE8F92E4D5BULL, + 0x353F57ABC4BEEA7EULL, + 0x735769D6DA5690CEULL, + 0x0A234AA642391484ULL, + 0xF6F9508028F80D9DULL, + 0xB8E319A27AB3F215ULL, + 0x31AD9C1151341A4DULL, + 0x773C22A57BEF5805ULL, + 0x45C7561A07968633ULL, + 0xF913DA9E249DBE36ULL, + 0xDA652D9B78A64C68ULL, + 0x4C27A97F3BC334EFULL, + 0x76621220E66B17F4ULL, + 0x967743899ACD7D0BULL, + 0xF3EE5BCAE0ED6782ULL, + 0x409F753600C879FCULL, + 0x06D09A39B5926DB6ULL, + 0x6F83AEB0317AC588ULL, + 0x01E6CA4A86381F21ULL, + 0x66FF3462D19F3025ULL, + 0x72207C24DDFD3BFBULL, + 0x4AF6B6D3E2ECE2EBULL, + 0x9C994DBEC7EA08DEULL, + 0x49ACE597B09A8BC4ULL, + 0xB38C4766CF0797BAULL, + 0x131B9373C57C2A75ULL, + 0xB1822CCE61931E58ULL, + 0x9D7555B909BA1C0CULL, + 0x127FAFDD937D11D2ULL, + 0x29DA3BADC66D92E4ULL, + 0xA2C1D57154C2ECBCULL, + 0x58C5134D82F6FE24ULL, + 0x1C3AE3515B62274FULL, + 0xE907C82E01CB8126ULL, + 0xF8ED091913E37FCBULL, + 0x3249D8F9C80046C9ULL, + 0x80CF9BEDE388FB63ULL, + 0x1881539A116CF19EULL, + 0x5103F3F76BD52457ULL, + 0x15B7E6F5AE47F7A8ULL, + 0xDBD7C6DED47E9CCFULL, + 0x44E55C410228BB1AULL, + 0xB647D4255EDB4E99ULL, + 0x5D11882BB8AAFC30ULL, + 0xF5098BBB29D3212AULL, + 0x8FB5EA14E90296B3ULL, + 0x677B942157DD025AULL, + 0xFB58E7C0A390ACB5ULL, + 0x89D3674C83BD4A01ULL, + 0x9E2DA4DF4BF3B93BULL, + 0xFCC41E328CAB4829ULL, + 0x03F38C96BA582C52ULL, + 0xCAD1BDBD7FD85DB2ULL, + 0xBBB442C16082AE83ULL, + 0xB95FE86BA5DA9AB0ULL, + 0xB22E04673771A93FULL, + 0x845358C9493152D8ULL, + 0xBE2A488697B4541EULL, + 0x95A2DC2DD38E6966ULL, + 0xC02C11AC923C852BULL, + 0x2388B1990DF2A87BULL, + 0x7C8008FA1B4F37BEULL, + 0x1F70D0C84D54E503ULL, + 0x5490ADEC7ECE57D4ULL, + 0x002B3C27D9063A3AULL, + 0x7EAEA3848030A2BFULL, + 0xC602326DED2003C0ULL, + 0x83A7287D69A94086ULL, + 0xC57A5FCB30F57A8AULL, + 0xB56844E479EBE779ULL, + 0xA373B40F05DCBCE9ULL, + 0xD71A786E88570EE2ULL, + 0x879CBACDBDE8F6A0ULL, + 0x976AD1BCC164A32FULL, + 0xAB21E25E9666D78BULL, + 0x901063AAE5E5C33CULL, + 0x9818B34448698D90ULL, + 0xE36487AE3E1E8ABBULL, + 0xAFBDF931893BDCB4ULL, + 0x6345A0DC5FBBD519ULL, + 0x8628FE269B9465CAULL, + 0x1E5D01603F9C51ECULL, + 0x4DE44006A15049B7ULL, + 0xBF6C70E5F776CBB1ULL, + 0x411218F2EF552BEDULL, + 0xCB0C0708705A36A3ULL, + 0xE74D14754F986044ULL, + 0xCD56D9430EA8280EULL, + 0xC12591D7535F5065ULL, + 0xC83223F1720AEF96ULL, + 0xC3A0396F7363A51FULL +}; + diff --git a/src/tiger.h b/src/tiger.h new file mode 100644 index 0000000..b8381c2 --- /dev/null +++ b/src/tiger.h @@ -0,0 +1,26 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + + +#ifndef HAVE_UHUB_HASH_TIGER_H +#define HAVE_UHUB_HASH_TIGER_H + +extern void tiger(uint64_t* str, uint64_t length, uint64_t* res); + +#endif /* HAVE_UHUB_HASH_TIGER_H */ diff --git a/src/uhub.h b/src/uhub.h new file mode 100644 index 0000000..d8c429e --- /dev/null +++ b/src/uhub.h @@ -0,0 +1,168 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#ifndef HAVE_UHUB_COMMON_H +#define HAVE_UHUB_COMMON_H + +/* Debugging */ +/* #define NETWORK_DUMP_DEBUG */ +/* #define MEMORY_DEBUG */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#if defined(__CYGWIN__) || defined(__MINGW32__) +#ifndef WINSOCK +#define WINSOCK +#endif +#endif + +#ifdef WINSOCK +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef WIN32 +#include +#include +#define HAVE_STRNDUP +#define HAVE_MEMMEM +#endif + +#ifdef SSL_SUPPORT +#include +#endif + +#include "../version.h" + +#define uhub_assert assert + +#include + +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) +#undef HAVE_STRNDUP +#undef HAVE_MEMMEM +#endif + +#ifdef MSG_NOSIGNAL +#define UHUB_SEND_SIGNAL MSG_NOSIGNAL +#else +#ifdef MSG_NOPIPE +#define UHUB_SEND_SIGNAL MSG_NOPIPE +#else +#define UHUB_SEND_SIGNAL 0 +#endif +#endif + + +#define SERVER_PORT 1511 +#define SERVER_ADDR_IPV6 "::" +#define SERVER_ADDR_IPV4 "0.0.0.0" +#define SERVER_BACKLOG 50 + +#ifndef WIN32 +#define SERVER_CONFIG "/etc/uhub/uhub.conf" +#define SERVER_ACL_FILE "/etc/uhub/users.conf" +#else +#define SERVER_CONFIG "uhub.conf" +#define SERVER_ACL_FILE "users.conf" +#ifndef stderr +#define stderr stdout +#endif +#endif + +#define TIMEOUT_CONNECTED 15 +#define TIMEOUT_HANDSHAKE 30 +#define TIMEOUT_SENDQ 120 +#define TIMEOUT_IDLE 7200 +#define TIMEOUT_STATS 3600 + +#define MAX_CLIENTS 512 +#define MAX_CID_LEN 39 +#define MAX_NICK_LEN 64 +#define MAX_UA_LEN 32 +#define TIGERSIZE 24 + +#define MAX_RECV_BUF 65535 + +#ifndef INET6_ADDRSTRLEN +#define INET6_ADDRSTRLEN 46 +#endif + +#include "adcconst.h" + +#define MIN(a, b) (a < b ? a : b) +#define MAX(a, b) (a > b ? a : b) + +#ifdef __cplusplus +extern "C" { +#endif + +#include "memory.h" +#include "misc.h" +#include "eventid.h" +#include "eventqueue.h" +#include "ipcalc.h" +#include "list.h" +#include "sid.h" +#include "network.h" +#include "netevent.h" +#include "auth.h" +#include "tiger.h" +#include "config.h" +#include "log.h" +#include "user.h" +#include "usermanager.h" +#include "message.h" +#include "route.h" +#include "hub.h" +#include "commands.h" +#include "inf.h" +#include "hubevent.h" + +#ifdef __cplusplus +} +#endif + +#endif /* HAVE_UHUB_COMMON_H */ + + + diff --git a/src/user.c b/src/user.c new file mode 100644 index 0000000..77f0a58 --- /dev/null +++ b/src/user.c @@ -0,0 +1,308 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#include "uhub.h" + +struct user* user_create(struct hub_info* hub, int sd) +{ + struct user* user = NULL; + + hub_log(log_trace, "user_create(), hub=%p, sd=%d", hub, sd); + + user = (struct user*) hub_malloc_zero(sizeof(struct user)); + + if (user == NULL) + return NULL; /* OOM */ + + user->ev_write = hub_malloc_zero(sizeof(struct event)); + user->ev_read = hub_malloc_zero(sizeof(struct event)); + + if (!user->ev_write || !user->ev_read) + { + hub_free(user->ev_read); + hub_free(user->ev_write); + hub_free(user); + return NULL; + } + + user->sd = sd; + user->tm_connected = time(NULL); + user->hub = hub; + user->feature_cast = 0; + + user->send_queue = list_create(); + user->send_queue_offset = 0; + user->send_queue_size = 0; + user->recv_buf_offset = 0; + user->recv_buf = 0; + + user_set_state(user, state_protocol); + return user; +} + +static void clear_send_queue_callback(void* ptr) +{ + adc_msg_free((struct adc_message*) ptr); +} + +void user_destroy(struct user* user) +{ + hub_log(log_trace, "user_destroy(), user=%p", user); + + if (user->ev_write) + { + event_del(user->ev_write); + hub_free(user->ev_write); + user->ev_write = 0; + } + + if (user->ev_read) + { + event_del(user->ev_read); + hub_free(user->ev_read); + user->ev_read = 0; + } + + net_close(user->sd); + + adc_msg_free(user->info); + user_clear_feature_cast_support(user); + + if (user->recv_buf) + { + hub_free(user->recv_buf); + } + + if (user->send_queue) + { + list_clear(user->send_queue, &clear_send_queue_callback); + list_destroy(user->send_queue); + } + + hub_free(user); +} + +void user_set_state(struct user* user, enum user_state state) +{ + if ((user->state == state_cleanup && state != state_disconnected) || (user->state == state_disconnected)) + { + puts("PANIC - Ignoring new state"); + return; + } + + user->state = state; +} + +void user_set_info(struct user* user, struct adc_message* cmd) +{ + adc_msg_free(user->info); + user->info = adc_msg_incref(cmd); +} + + +static int convert_support_fourcc(int fourcc) +{ + switch (fourcc) + { + case FOURCC('B','A','S','0'): /* Obsolete */ + case FOURCC('B','A','S','E'): + return feature_base; + + case FOURCC('A','U','T','0'): + return feature_auto; + + case FOURCC('U','C','M','0'): + case FOURCC('U','C','M','D'): + return feature_ucmd; + + case FOURCC('Z','L','I','F'): + return feature_zlif; + + case FOURCC('B','B','S','0'): + return feature_bbs; + + case FOURCC('T','I','G','R'): + return feature_tiger; + + case FOURCC('B','L','O','M'): + case FOURCC('B','L','O','0'): + return feature_bloom; + + case FOURCC('P','I','N','G'): + return feature_ping; + + case FOURCC('L','I','N','K'): + return feature_link; + + default: + hub_log(log_debug, "Unknown extension: %x", fourcc); + return 0; + } +} + +void user_support_add(struct user* user, int fourcc) +{ + int feature_mask = convert_support_fourcc(fourcc); + user->flags |= feature_mask; +} + +int user_flag_get(struct user* user, enum user_flags flag) +{ + return user->flags & flag; +} + +void user_flag_set(struct user* user, enum user_flags flag) +{ + user->flags |= flag; +} + +void user_flag_unset(struct user* user, enum user_flags flag) +{ + user->flags &= ~flag; +} + +void user_set_nat_override(struct user* user) +{ + user_flag_set(user, flag_nat); +} + +int user_is_nat_override(struct user* user) +{ + return user_flag_get(user, flag_nat); +} + +void user_support_remove(struct user* user, int fourcc) +{ + int feature_mask = convert_support_fourcc(fourcc); + user->flags &= ~feature_mask; +} + +void user_schedule_destroy(struct user* user) +{ + struct event_data post; + memset(&post, 0, sizeof(post)); + post.id = UHUB_EVENT_USER_DESTROY; + post.ptr = user; + event_queue_post(user->hub->queue, &post); +} + +void user_disconnect(struct user* user, int reason) +{ + struct event_data post; + int need_notify = 0; + + if (user_is_disconnecting(user)) + { + return; + } + + /* dont read more data from this user */ + if (user->ev_read) + { + event_del(user->ev_read); + hub_free(user->ev_read); + user->ev_read = 0; + } + + hub_log(log_trace, "user_disconnect(), user=%p, reason=%d, state=%d", user, reason, user->state); + + need_notify = user_is_logged_in(user); + user->quit_reason = reason; + user_set_state(user, state_cleanup); + + if (need_notify) + { + memset(&post, 0, sizeof(post)); + post.id = UHUB_EVENT_USER_QUIT; + post.ptr = user; + event_queue_post(user->hub->queue, &post); + } + else + { + user->quit_reason = quit_unknown; + user_schedule_destroy(user); + } + + +} + +int user_have_feature_cast_support(struct user* user, char feature[4]) +{ + char* tmp = list_get_first(user->feature_cast); + while (tmp) + { + if (strncmp(tmp, feature, 4) == 0) + return 1; + + tmp = list_get_next(user->feature_cast); + } + + return 0; +} + +int user_set_feature_cast_support(struct user* u, char feature[4]) +{ + if (!u->feature_cast) + { + u->feature_cast = list_create(); + } + + if (!u->feature_cast) + { + return 0; /* OOM! */ + } + + list_append(u->feature_cast, hub_strndup(feature, 4)); + return 1; +} + +void user_clear_feature_cast_support(struct user* u) +{ + if (u->feature_cast) + { + list_clear(u->feature_cast, &hub_free); + list_destroy(u->feature_cast); + u->feature_cast = 0; + } +} + +int user_is_logged_in(struct user* user) +{ + if (user->state == state_normal) + return 1; + return 0; +} + +int user_is_connecting(struct user* user) +{ + if (user->state == state_protocol || user->state == state_identify || user->state == state_verify) + return 1; + return 0; +} + +int user_is_disconnecting(struct user* user) +{ + if (user->state == state_cleanup || user->state == state_disconnected) + return 1; + return 0; +} + + + + diff --git a/src/user.h b/src/user.h new file mode 100644 index 0000000..84075b2 --- /dev/null +++ b/src/user.h @@ -0,0 +1,261 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#ifndef HAVE_UHUB_USER_H +#define HAVE_UHUB_USER_H + + +struct hub_info; + + +enum user_state +{ + state_protocol = 0, /**<< "User must send a valid protocol handshake" */ + state_identify = 1, /**<< "User must send identification message (INF) " */ + state_verify = 2, /**<< "User must send password to verify identification" */ + state_normal = 3, /**<< "User is logged in." */ + state_cleanup = 4, /**<< "User is disconnected, but other users need to be notified." */ + state_disconnected = 5, /**<< "User is disconnected" */ +}; + + +enum user_flags +{ + feature_base = 0x00000001, /** BASE: Basic configuration (required by all clients) */ + feature_auto = 0x00000002, /** AUT0: Automatic nat detection traversal */ + feature_bbs = 0x00000004, /** BBS0: Bulletin board system (not supported) */ + feature_ucmd = 0x00000008, /** UCMD: User commands (not supported by this software) */ + feature_zlif = 0x00000010, /** ZLIF: gzip stream compression (not supported) */ + feature_tiger = 0x00000020, /** TIGR: Client supports the tiger hash algorithm */ + feature_bloom = 0x00000040, /** BLO0: Bloom filter (not supported) */ + feature_ping = 0x00000080, /** PING: Hub pinger information extension */ + feature_link = 0x00000100, /** LINK: Hub link (not supported) */ + flag_ignore = 0x01000000, /** Ignore further reads */ + flag_choke = 0x02000000, /** Choked: Cannot send, waiting for write event */ + flag_want_read = 0x04000000, /** Need to read (SSL) */ + flag_want_write = 0x08000000, /** Need to write (SSL) */ + flag_user_list = 0x10000000, /** Send queue bypass (when receiving the send queue) */ + flag_nat = 0x20000000, /** nat override enabled */ +}; + + +enum user_quit_reason +{ + quit_unknown = 0, + quit_disconnected = 1, /** User disconnected */ + quit_kicked = 2, /** User was kicked */ + quit_banned = 3, /** User was banned */ + quit_timeout = 4, /** User timed out (no data for a while) */ + quit_send_queue = 5, /** User's send queue was overflowed */ + quit_memory_error = 6, /** Not enough memory available */ + quit_socket_error = 7, /** A socket error occured */ + quit_protocol_error = 8, /** Fatal protocol error */ + quit_logon_error = 9, /** Unable to login (wrong password, CID/PID, etc) */ + quit_hub_disabled = 10, /** Hub is disabled. No new connections allowed */ +}; + + +struct user_info +{ + sid_t sid; /** session ID */ + char cid[MAX_CID_LEN+1]; /** global client ID */ + char nick[MAX_NICK_LEN+1]; /** User's nick name */ +}; + +/** + * This struct contains additional information about the user, such + * as the number of bytes and files shared, and the number of hubs the + * user is connected to, etc. + */ +struct user_counts +{ + uint64_t shared_size; /** Shared size in bytes */ + size_t shared_files; /** The number of shared files */ + size_t upload_slots; /** The number of upload slots */ + size_t hub_count_user; /** The number of hubs connected as user */ + size_t hub_count_registered; /** The number of hubs connected as registered user */ + size_t hub_count_operator; /** The number of hubs connected as operator */ + size_t hub_count_total; /** The number of hubs connected to in total */ +}; + +struct user +{ + int sd; /** socket descriptor */ + struct event* ev_read; /** libevent struct for read events */ + struct event* ev_write; /** libevent struct for write events */ + enum user_state state; /** see enum user_state */ + enum user_credentials credentials; /** see enum user_credentials */ + struct user_info id; /** Contains nick name and CID */ + int flags; /** see enum user_features */ + char user_agent[MAX_UA_LEN+1];/** User agent string */ + time_t tm_connected; /** time when user connected */ + time_t tm_last_read; /** time the user last received something from the hub */ + time_t tm_last_write; /** time the user last sent something to the hub */ + struct linked_list* feature_cast; /** Features supported by feature cast */ + struct adc_message* info; /** ADC 'INF' message (broadcasted to everyone joining the hub) */ + size_t send_queue_offset; /** Send queue byte offset */ + struct linked_list* send_queue; /** Send queue */ + int send_queue_size; /** Size of send queue (in bytes, not messages) */ + int send_queue_esize; /** Effective send queue size */ + char* recv_buf; /** Recv buffer */ + size_t recv_buf_offset; /** Recv buffer offset */ + struct hub_info* hub; /** The hub instance this user belong to */ + int quit_reason; /** Quit reason (see user_quit_reason) */ + struct ip_addr_encap ipaddr; /** IP address of connected user */ + struct user_counts limits; /** Data used for limitation */ +#ifdef SSL_SUPPORT + SSL* ssl; /** SSL handle */ +#endif /* SSL_SUPPORT */ +}; + + +/** + * Create a user with the given socket descriptor. + * This basically only allocates memory and initializes all variables + * to an initial state. + * + * state is set to state_protocol. + * + * @param sd socket descriptor associated with the user + * @return User object or NULL if not enough memory is available. + */ +extern struct user* user_create(struct hub_info* hub, int sd); + +/** + * Delete a user. + * + * !WRONG! If the user is logged in a quit message is issued. + */ +extern void user_destroy(struct user* user); + +/** + * Will post a message that will delete the user later. + */ +extern void user_schedule_destroy(struct user* user); + +/** + * Disconnect a user. + * This will mark the user connection ready for being terminated. + * A reason can be given using the enum user_quit_reason. + * + * Things to be done when calling this: + * - Mark the user with state_cleanup + * + * If the user is logged in to the hub: + * - post message: UHUB_EVENT_USER_QUIT + * + * @param user User to disconnect + * @param reason See enum user_quit_reason + */ +extern void user_disconnect(struct user* user, int reason); + +/** + * This associates a INF message to the user. + * If the user already has a INF message associated, then this is + * released before setting the new one. + */ +extern void user_set_info(struct user* user, struct adc_message* info); + +/** + * Specify a user's state. + * NOTE: DON'T, unless you know what you are doing. + */ +extern void user_set_state(struct user* user, enum user_state); + +/** + * Returns 1 if the user is in state state_normal, or 0 otherwise. + */ +extern int user_is_logged_in(struct user* user); + +/** + * Returns 1 if the user is in state_protocol, state_identify or state_verify. + * Returns 0 otherwise. + */ +extern int user_is_connecting(struct user* user); + +/** + * Returns 1 only if the user is in state_cleanup or state_disconnected. + */ +extern int user_is_disconnecting(struct user* user); + +/** + * User supports the protocol extension as given in fourcc. + * This is usually set while the user is connecting, but can + * also be used to subscribe to a new class of messages from the + * hub. + * + * @see enum user_flags + */ +extern void user_support_add(struct user* user, int fourcc); + +/** + * User no longer supports the protocol extension as given in fourcc. + * This can be used to unsubscribe to certain messages generated by + * the hub. + * @see enum user_flags + */ +extern void user_support_remove(struct user* user, int fourcc); + +/** + * Sets the nat override flag for a user, this allows users on the same + * subnet as a natted hub to spoof their IP in order to use active mode + * on a natted hub. + */ +extern void user_set_nat_override(struct user* user); +extern int user_is_nat_override(struct user* user); + +/** + * Set a flag. @see enum user_flags + */ +extern void user_flag_set(struct user* user, enum user_flags flag); +extern void user_flag_unset(struct user* user, enum user_flags flag); + +/** + * Get a flag. @see enum user_flags + */ +extern int user_flag_get(struct user* user, enum user_flags flag); + +/** + * Check if a user supports 'feature' for feature casting (basis for 'Fxxx' messages) + * The feature cast is specified as the 'SU' argument to the user's + * INF-message. + * + * @param feature a feature to lookup (example: 'TCP4' or 'UDP4') + * @return 1 if 'feature' supported, or 0 otherwise + */ +extern int user_have_feature_cast_support(struct user* user, char feature[4]); + +/** + * Set feature cast support for feature. + * + * @param feature a feature to lookup (example: 'TCP4' or 'UDP4') + * @return 1 if 'feature' supported, or 0 otherwise + */ +extern int user_set_feature_cast_support(struct user* u, char feature[4]); + +/** + * Remove all feature cast support features. + */ +extern void user_clear_feature_cast_support(struct user* u); + + + +#endif /* HAVE_UHUB_USER_H */ + + diff --git a/src/usermanager.c b/src/usermanager.c new file mode 100644 index 0000000..8fbdd93 --- /dev/null +++ b/src/usermanager.c @@ -0,0 +1,222 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#include "uhub.h" + +/* + * This callback function is used to clear user objects from the userlist. + * Should only be used in user_manager_shutdown(). + */ +static void clear_user_list_callback(void* ptr) +{ + if (ptr) + { + struct user* u = (struct user*) ptr; + + /* Mark the user as already being disconnected. + * This prevents the hub from trying to send + * quit messages to other users. + */ + u->credentials = cred_none; + user_destroy(u); + } +} + + +void user_manager_stats(struct hub_info* hub) +{ + int factor = 0; + struct net_statistics* total; + struct net_statistics* intermediate; + net_stats_get(&intermediate, &total); + + factor = (time(NULL) - intermediate->timestamp); + if (!factor) factor++; + + hub_log(log_info, "Statistics users=%zu, net_tx=%d KB/s, net_rx=%d KB/s", + hub->users->count, + (int) ((intermediate->tx / factor) / 1024), + (int) ((intermediate->rx / factor) / 1024)); + + net_stats_reset(); +} + + +static void timer_statistics(int fd, short ev, void *arg) +{ + struct hub_info* hub = (struct hub_info*) arg; + struct timeval timeout = { TIMEOUT_STATS, 0 }; + user_manager_stats(hub); + evtimer_set(&hub->ev_timer, timer_statistics, hub); + evtimer_add(&hub->ev_timer, &timeout); +} + + +int user_manager_init(struct hub_info* hub) +{ + struct user_manager* users = NULL; + struct timeval timeout = { TIMEOUT_STATS, 0 }; + + users = (struct user_manager*) hub_malloc_zero(sizeof(struct user_manager)); + + users->list = list_create(); + users->free_sid = 1; + + if (!users->list) + { + list_destroy(users->list); + return -1; + } + + hub->users = users; + + evtimer_set(&hub->ev_timer, timer_statistics, hub); + evtimer_add(&hub->ev_timer, &timeout); + return 0; +} + + +void user_manager_shutdown(struct hub_info* hub) +{ + struct user_manager* users = hub->users; + event_del(&hub->ev_timer); + + list_clear(users->list, &clear_user_list_callback); + list_destroy(users->list); + hub_free(hub->users); +} + + +void user_manager_add(struct user* user) +{ + list_append(user->hub->users->list, user); + user->hub->users->count++; + user->hub->users->count_peak = MAX(user->hub->users->count, user->hub->users->count_peak); + + user->hub->users->shared_size += user->limits.shared_size; + user->hub->users->shared_files += user->limits.shared_files; +} + +void user_manager_remove(struct user* user) +{ + list_remove(user->hub->users->list, user); + user->hub->users->count--; + + user->hub->users->shared_size -= user->limits.shared_size; + user->hub->users->shared_files -= user->limits.shared_files; +} + + +struct user* get_user_by_sid(struct hub_info* hub, sid_t sid) +{ + struct user* user = (struct user*) list_get_first(hub->users->list); /* iterate users */ + while (user) + { + if (user->id.sid == sid) + return user; + user = (struct user*) list_get_next(hub->users->list); + } + return NULL; +} + + +struct user* get_user_by_cid(struct hub_info* hub, const char* cid) +{ + struct user* user = (struct user*) list_get_first(hub->users->list); /* iterate users - only on incoming INF msg */ + while (user) + { + if (strcmp(user->id.cid, cid) == 0) + return user; + user = (struct user*) list_get_next(hub->users->list); + } + return NULL; +} + + +struct user* get_user_by_nick(struct hub_info* hub, const char* nick) +{ + struct user* user = (struct user*) list_get_first(hub->users->list); /* iterate users - only on incoming INF msg */ + while (user) + { + if (strcmp(user->id.nick, nick) == 0) + return user; + user = (struct user*) list_get_next(hub->users->list); + } + return NULL; +} + + +int send_user_list(struct user* target) +{ + int ret = 1; + user_flag_set(target, flag_user_list); + struct user* user = (struct user*) list_get_first(target->hub->users->list); /* iterate users - only on INF or PAS msg */ + while (user) + { + if (user_is_logged_in(user)) + { + ret = route_to_user(target, user->info); + if (!ret) + break; + } + user = (struct user*) list_get_next(user->hub->users->list); + } + + if (!target->send_queue_size) + { + user_flag_unset(target, flag_user_list); + } + return ret; +} + + +void send_quit_message(struct user* leaving) +{ + struct adc_message* command = adc_msg_construct(ADC_CMD_IQUI, 6); + adc_msg_add_argument(command, (const char*) sid_to_string(leaving->id.sid)); + + if (leaving->quit_reason == quit_banned || leaving->quit_reason == quit_kicked) + { + adc_msg_add_argument(command, ADC_QUI_FLAG_DISCONNECT); + } + + route_to_all(leaving->hub, command); + adc_msg_free(command); +} + + +sid_t user_manager_get_free_sid(struct hub_info* hub) +{ +#if 0 + struct user* user; + user = (struct user*) list_get_first(hub->users->list); /* iterate normal users */ + while (user) + { + if (user->sid == hub->users->free_sid) + { + hub->users->free_sid++; + if (hub->users->free_sid >= SID_MAX) hub->users->free_sid = 1; + break; + } + user = (struct user*) list_get_next(hub->users->list); + } +#endif + return hub->users->free_sid++; +} + diff --git a/src/usermanager.h b/src/usermanager.h new file mode 100644 index 0000000..07207b6 --- /dev/null +++ b/src/usermanager.h @@ -0,0 +1,101 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2009, Jan Vidar Krey + * + * 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 . + * + */ + +#ifndef HAVE_UHUB_USER_MANAGER_H +#define HAVE_UHUB_USER_MANAGER_H + +struct user_manager +{ + size_t count; /**<< "Number of all fully connected and logged in users" */ + size_t count_peak; /**<< "Peak number of users" */ + sid_t free_sid; /**<< "The next available SID." */ + uint64_t shared_size; /**<< "The total number of shared bytes among fully connected users." */ + uint64_t shared_files; /**<< "The total number of shared files among fully connected users." */ + struct linked_list* list; /**<< "Contains all users" */ +}; + +/** + * Initializes the user manager. + * @return 0 on success, or -1 if error (out of memory). + */ +extern int user_manager_init(struct hub_info* hub); + +/** + * Shuts down the user manager. + * All users will be disconnected and deleted as part of this. + */ +extern void user_manager_shutdown(struct hub_info* hub); + +/** + * Generate statistics for logfiles. + */ +extern void user_manager_stats(struct hub_info* hub); + +/** + * Add a new user to the user manager. + */ +extern void user_manager_add(struct user* user); + +/** + * Remove a user from the user manager. + * This user is connected, and will be moved to the leaving queue, pending + * all messages in the message queue, and resource cleanup. + */ +extern void user_manager_remove(struct user* user); + +/** + * Returns a free sid for a new user. + */ +extern sid_t user_manager_get_free_sid(struct hub_info* hub); + +/** + * Lookup a user based on the session ID (sid). + * NOTE: This will only search connected users. + * @return a user if found, or NULL if not found + */ +extern struct user* get_user_by_sid(struct hub_info* hub, sid_t sid); + +/** + * Lookup a user based on the client ID (cid). + * @return a user if found, or NULL if not found + */ +extern struct user* get_user_by_cid(struct hub_info* hub, const char* cid); + +/** + * Lookup a user based on the nick name. + * @return a user if found, or NULL if not found + */ +extern struct user* get_user_by_nick(struct hub_info* hub, const char* nick); + +/** + * Send the user list of connected clients to 'user'. + * Usually part of the login process. + * + * @return 1 if sending the user list succeeded, 0 otherwise. + */ +extern int send_user_list(struct user* user); + +/** + * Send a quit message to all connected users when 'user' is + * leaving the hub (for whatever reason). + */ +extern void send_quit_message(struct user* user); + + +#endif /* HAVE_UHUB_USER_MANAGER_H */ diff --git a/version.h b/version.h new file mode 100644 index 0000000..76aae78 --- /dev/null +++ b/version.h @@ -0,0 +1,15 @@ +#ifndef PRODUCT +#define PRODUCT "uHub" +#endif + +#ifndef PRODUCT_TITLE +#define PRODUCT_TITLE "(micro-Hub)" +#endif + +#ifndef VERSION +#define VERSION "0.2.6-alpha" +#endif + +#ifndef COPYRIGHT +#define COPYRIGHT "Copyright (c) 2007-2009, Jan Vidar Krey " +#endif